home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / SciAn / src / ScianSpaces.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  249KB  |  9,355 lines

  1. /*ScianSpaces.c
  2.   Space objects
  3.   Eric Pepke
  4.   April 6, 1990
  5. */
  6.  
  7. #include "Scian.h"
  8. #include "ScianTypes.h"
  9. #include "ScianColors.h"
  10. #include "ScianLists.h"
  11. #include "ScianArrays.h"
  12. #include "ScianLights.h"
  13. #include "ScianSpaces.h"
  14. #include "ScianEvents.h"
  15. #include "ScianWindows.h"
  16. #include "ScianDraw.h"
  17. #include "ScianObjWindows.h"
  18. #include "ScianVisWindows.h"
  19. #include "ScianVisObjects.h"
  20. #include "ScianDialogs.h"
  21. #include "ScianScripts.h"
  22. #include "ScianIcons.h"
  23. #include "ScianIDs.h"
  24. #include "ScianControls.h"
  25. #include "ScianButtons.h"
  26. #include "ScianTextBoxes.h"
  27. #include "ScianTitleBoxes.h"
  28. #include "ScianFontSystem.h"
  29. #include "ScianPerspec.h"
  30. #include "ScianErrors.h"
  31. #include "ScianTimers.h"
  32. #include "ScianSliders.h"
  33. #include "ScianStyle.h"
  34. #include "ScianComplexControls.h"
  35. #include "ScianMethods.h"
  36. #include "ScianPick.h"
  37. #include "ScianPreferences.h"
  38. #include "ScianFiles.h"
  39. #include "ScianObjFunctions.h"
  40. #include "ScianTemplates.h"
  41. #include "ScianTemplateHelper.h"
  42. #include "ScianSymbols.h"
  43. #include "ScianDepend.h"
  44. #include "ScianDatasets.h"
  45. #include "ScianFilters.h"
  46. #include "ScianDatabase.h"
  47. #include "ScianAnimation.h"
  48. #include "ScianPictures.h"
  49. #include "ScianSnap.h"
  50.  
  51. Bool phsco = false;            /*True iff phscologram is on*/
  52. Bool drawingTransparent = false;    /*True iff drawing only transparent*/
  53. int clockDisplaySerialNum = 0;        /*Clock display serial number*/
  54. extern real fps;
  55. ObjPtr spacePickedObject = NULLOBJ;
  56.  
  57. int qualityDrawnSoFar;        /*Max quality drawn so far*/
  58. int minDrawingQuality;        /*Minimum quality to draw*/
  59. int maxDrawingQuality;        /*Maximum quality to draw*/
  60.  
  61. #define PLAYCLOCK (0.1)            /*Rate for clock play*/
  62. #define FASTCLOCK (0.2)            /*Rate for clock fast*/
  63.  
  64. #define ROTLIGHTS            /*Rotate lights with object*/
  65.  
  66. #define DSPCLOCKWIDTH    97
  67. #define DSPCLOCKHEIGHT    48
  68. #define DSPCLOCKLEFT    55
  69. #define DSPCLOCKTOP    70
  70. #define DSPCLOCKTLEFT    10
  71. #define DSPCLOCKTRIGHT    10
  72. #define DSPCLOCKTTOP    23
  73.  
  74. /*Dial factors*/
  75. #define DIALROTFACTOR    0.02    /*Factor for rotating with dials*/
  76. #define DIALMOVEFACTOR    0.01    /*Factor for moving with dials*/
  77. #define DIALANGLEFACTOR    0.001    /*Factor for changing angles*/
  78. #define DIALWIDTHFACTOR 0.005    /*Factor for changing width*/
  79. #define DIALBINOCFACTOR    0.01    /*Factor for changing binocularity*/
  80.  
  81. /*Stereo and blanking values for CrystalEyes*/
  82. #define YSTEREO        491
  83. #define YBLANK        41
  84.  
  85. #define ASPECT        (((float) (right - left)) / ((float) (top - bottom))) * (phsco ? 0.61 : 1.0)
  86. #define MINROT        3        /*Minimum number of pixels to move to rotate*/
  87.  
  88. ObjPtr curSpace = 0;            /*Current space*/
  89. real curFocusDist = 0.0;        /*Current focus distance*/
  90. real curViewField = 0.0;        /*Current field of view.  Angle if 
  91.                       perspec, width if ortho*/
  92. real curViewClip[2];            /*Current viewing clipping distances*/
  93. ObjPtr curSpaceLights;            /*Lights in the current space*/
  94. int curWhichView;            /*Current which view*/
  95. ObjPtr spaceClass = 0;            /*Class of a 3-D space*/
  96. real spaceTime = 0.0;            /*Time for current space.  Cute name, huh?*/
  97. ObjPtr spacePanelClass;            /*Invisible panel over a space*/
  98. ObjPtr spaceBackPanelClass;        /*Colored panel behind a space*/
  99. ObjPtr controllerClass;            /*Class for space controllers (clocks, lights, etc.)*/
  100. ObjPtr clockClass;            /*Class of clocks*/
  101. ObjPtr placementClass;            /*Class of object with placement*/
  102. ObjPtr stereoClass;            /*Class of object with stereo*/
  103. ObjPtr observerClass;            /*Class of observers*/
  104. ObjPtr rendererClass;            /*Class of renderers*/
  105. Matrix viewerCoordsMatrix;        /*Matrix in viewer coordinates*/
  106. Bool oneObserver = false;
  107. Bool oneRenderer = false;
  108. Bool oneClock = false;
  109. Bool oneLights = false;            /*True iff to make only one controller*/
  110. ObjPtr placementTrackClass;        /*Track for observer placement*/
  111. ObjPtr jointClass;            /*Class of joints*/
  112. real observerPosition[3];
  113.  
  114. void DrawSpaceContents(ObjPtr, int, int, int, int, int);
  115.  
  116. #ifdef PROTO
  117. static double f(double x)
  118. #else
  119. static double f(x)
  120. double x;
  121. #endif
  122. /*Performs the increasing monotonic function required for the virtual trackball
  123.   of click radius ratio x.  Yeah, right.*/
  124. {
  125.     if (x <= 0.0)
  126.     {
  127.     return 0.0;
  128.     }
  129.     else if (x >= 1.0)
  130.     {
  131.     return M_PI_2;
  132.     }
  133.     else
  134.     {
  135.     return M_PI_2 * x;
  136.     }
  137. }
  138.  
  139. Bool GetListExtent(list, bounds)
  140. ObjPtr list;
  141. real bounds[];
  142. /*Puts the extent of the objects (or the objects they represent) in list 
  143.   into bounds.  Returns true iff it's nonzero*/
  144. {
  145.     ThingListPtr runner;
  146.  
  147.     /*Make bounds ludicrous*/
  148.     bounds[0] = bounds[2] = bounds[4] = plusInf;
  149.     bounds[1] = bounds[3] = bounds[5] = minusInf;
  150.  
  151.     runner = LISTOF(list);
  152.     if (!runner)
  153.     {
  154.     return false;
  155.     }
  156.     while (runner)
  157.     {
  158.     ObjPtr repObj;
  159.     ObjPtr var;
  160.  
  161.     repObj = runner -> thing;
  162.     if (IsObject(repObj))
  163.     {
  164.         MakeVar(repObj, BOUNDS);
  165.         var = GetVar(repObj, BOUNDS);
  166.         if (var)
  167.         {
  168.         real localBounds[6];
  169.         Array2CArray(localBounds, var);
  170.         
  171.         var = GetVar(repObj, XSCALE);
  172.         if (var)
  173.         {
  174.             real v;
  175.             v = GetReal(var);
  176.             localBounds[0] *= v;
  177.             localBounds[1] *= v;
  178.         }
  179.         var = GetVar(repObj, YSCALE);
  180.         if (var)
  181.         {
  182.             real v;
  183.             v = GetReal(var);
  184.             localBounds[2] *= v;
  185.             localBounds[3] *= v;
  186.         }
  187.         var = GetVar(repObj, ZSCALE);
  188.         if (var)
  189.         {
  190.             real v;
  191.             v = GetReal(var);
  192.             localBounds[4] *= v;
  193.             localBounds[5] *= v;
  194.         }
  195.         if (localBounds[0] < bounds[0]) bounds[0] = localBounds[0];
  196.         if (localBounds[1] > bounds[1]) bounds[1] = localBounds[1];
  197.         if (localBounds[2] < bounds[2]) bounds[2] = localBounds[2];
  198.         if (localBounds[3] > bounds[3]) bounds[3] = localBounds[3];
  199.         if (localBounds[4] < bounds[4]) bounds[4] = localBounds[4];
  200.         if (localBounds[5] > bounds[5]) bounds[5] = localBounds[5];
  201.         }
  202.     }
  203.     runner = runner -> next;
  204.     }
  205.  
  206.     /*If bounds ludicrous, make them sane*/
  207.     if (bounds[0] == plusInf || bounds[2] == plusInf || bounds[4] == plusInf ||
  208.     bounds[1] == minusInf || bounds[3] == minusInf || bounds[5] == minusInf)
  209.     {
  210.     bounds[0] = bounds[2] = bounds[4] = 0.0;
  211.     bounds[1] = bounds[3] = bounds[5] = 1.0;
  212.     }
  213.  
  214.     return true;
  215. }
  216.  
  217. #define NUDGESIZE    2000        /*Size of one nudge*/
  218. #define MAXNNUDGES    4        /*Maximum number of nudges*/
  219. long zMin = 0 + NUDGESIZE * MAXNNUDGES;
  220. long zMax = 0x7fffff - NUDGESIZE * MAXNNUDGES;
  221. long curZMin, curZMax;
  222.  
  223. void NudgeCloser()
  224. /*Nudges objects to be drawn closer*/
  225. {
  226. #ifdef GRAPHICS
  227.     curZMax -= NUDGESIZE;
  228.     curZMin -= NUDGESIZE;
  229.     lsetdepth(curZMin, curZMax);
  230. #endif
  231. }
  232.  
  233. void NudgeFarther()
  234. /*Nudges objects to be drawn farther*/
  235. {
  236. #ifdef GRAPHICS
  237.     curZMax += NUDGESIZE;
  238.     curZMin += NUDGESIZE;
  239.     lsetdepth(curZMin, curZMax);
  240. #endif
  241. }
  242.  
  243. #ifdef PROTO
  244. void upat(float vx, float vy, float vz, 
  245.           float px, float py, float pz, 
  246.           float ux, float uy, float uz)
  247. #else
  248. void upat(vx, vy, vz, px, py, pz, ux, uy, uz)
  249. float vx; float vy; float vz; 
  250. float px; float py; float pz; 
  251. float ux; float uy; float uz;
  252. #endif
  253. /*Replacement for lookat.  Specifies a viewer at (vx, vy, vz) looking at
  254.   a point (px, py, pz) with an up vector of (ux, uy, uz).  Adapted from 
  255.   Thant Tessman's code in 4Dgifts.*/
  256. {
  257. #ifdef GRAPHICS
  258.     int i;
  259.     float forward[3], side[3], up[3];
  260.     float m[4][4];
  261.  
  262.     forward[0] = px - vx;
  263.     forward[1] = py - vy;
  264.     forward[2] = pz - vz;
  265.  
  266.     /* temporarily use view-up to hold world-up */
  267.     up[0] = ux;        
  268.     up[1] = uy;
  269.     up[2] = uz;
  270.  
  271.     NORMALIZE(forward);
  272.  
  273.     /* generate the vector side from the
  274.      * 2 vectors view forward and world up 
  275.      */
  276.     CROSS(forward, up, side);
  277.     NORMALIZE(side);
  278.  
  279.     /* generate the view up vector from 
  280.      * the 2 vectors view forward and view side 
  281.      */
  282.     CROSS(side, forward, up);
  283.  
  284.     m[0][0] = side[0];
  285.     m[1][0] = side[1];
  286.     m[2][0] = side[2];
  287.     m[3][0] = 0.0;
  288.  
  289.     m[0][1] = up[0];
  290.     m[1][1] = up[1];
  291.     m[2][1] = up[2];
  292.     m[3][1] = 0.0;
  293.  
  294.     m[0][2] = -forward[0];
  295.     m[1][2] = -forward[1];
  296.     m[2][2] = -forward[2];
  297.     m[3][2] = 0.0;
  298.  
  299.     m[0][3] = 0.0;
  300.     m[1][3] = 0.0;
  301.     m[2][3] = 0.0;
  302.     m[3][3] = 1.0;
  303.     multmatrix(m);
  304.     translate(-vx, -vy, -vz);
  305. #endif
  306. }
  307.  
  308. #ifdef PROTO
  309. void StartSpace(ObjPtr space, int left, int right, int bottom, int top,
  310.     int action, int whichView)
  311. #else
  312. void StartSpace(space, left, right, bottom, top, action, whichView)
  313. ObjPtr space;
  314. int left, right, bottom, top;
  315. int action;
  316. int whichView;
  317. #endif
  318. /*Start a drawing, rotation, or press in a space.  Action is the action
  319.   that is being performed*/
  320. {
  321. #ifdef GRAPHICS
  322.     real eyePosn[3];        /*Coords of the eye*/
  323.     real focusPoint[3];        /*Coords of the focus point*/
  324.     real upVector[3];        /*The up vector of the observer*/
  325.     real forwardVector[3];
  326.     ObjPtr object;
  327.     ObjPtr psStuff;
  328.     ObjPtr boundsArray;
  329.     real bounds[4];
  330.     real bigBounds[6];        /*Bounds of the objects*/
  331.     Coord center[3];        /*The center of the data set*/
  332.     ObjPtr contents;
  333.     float scaleFactor;        /*Factor to scale*/
  334.     real maxSize;        /*Maximum size of the objects this draw*/
  335.     ObjPtr observer;        /*Observer of the space*/
  336.     real eyeOffset;
  337.     ObjPtr var;
  338.     Matrix proj, model;
  339.     float aspect;        /*Aspect ratio*/
  340.  
  341.     aspect = ASPECT;
  342.  
  343.     curWhichView = whichView;
  344.  
  345.     /*Change bounds and aspect ratio depending on whichView*/
  346.     switch(VIEWOF(whichView))
  347.     {
  348.     case VIEW_XLEFT:
  349.     case VIEW_WRIGHT:
  350.         left = (left + right) / 2 + 1;
  351.         aspect = ASPECT;
  352.         break;
  353.     case VIEW_XRIGHT:
  354.     case VIEW_WLEFT:
  355.         right = (left + right) / 2;
  356.         aspect = ASPECT;
  357.         break;
  358.     case VIEW_CLEFT:
  359.         bottom = YSTEREO + YBLANK;
  360.         top = bottom + YSTEREO;
  361.         break;
  362.     case VIEW_CRIGHT:
  363.         bottom = 0;
  364.         top = bottom + YSTEREO;
  365.         break;
  366.     }
  367.  
  368.     /*Set a new viewport to the current area*/
  369.     SetSubPort(left, right, bottom, top);
  370.  
  371.     observer = GetObjectVar("StartSpace", space, OBSERVER);
  372.     if (!observer) return;
  373.  
  374.     if (ISLEFT(whichView))
  375.     {
  376.         var = GetVar(observer, BINOCULARITY);
  377.         eyeOffset = -GetReal(var) * 0.5;
  378.     }
  379.     else if (ISRIGHT(whichView))
  380.     {
  381.         var = GetVar(observer, BINOCULARITY);
  382.         eyeOffset = GetReal(var) * 0.5;
  383.     }
  384.     else
  385.     {
  386.         eyeOffset = 0.0;
  387.     }
  388.  
  389.     /*Set up current viewing parameters*/
  390.     var = GetVar(observer, FOCUSDIST);
  391.     if (var)
  392.     {
  393.     curFocusDist = GetReal(var);
  394.     }
  395.     else
  396.     {
  397.     curFocusDist = INITEYEDIST;
  398.     }
  399.  
  400.     var = GetVar(observer, VIEWFIELD);
  401.     if (var)
  402.     {
  403.     curViewField = GetReal(var);
  404.     }
  405.     else
  406.     {
  407.     curViewField = INITAOV;
  408.     }
  409.  
  410.     var = GetVar(observer, VIEWCLIP);
  411.     if (var)
  412.     {
  413.     Array2CArray(curViewClip, var);
  414.     }
  415.     else
  416.     {
  417.     curViewClip[0] = INITNEARCLIP;
  418.     curViewClip[1] = INITFARCLIP;
  419.     }
  420.     if (curViewClip[0] < MINCLIP) curViewClip[0] = MINCLIP;
  421.     if (curViewClip[1] < MINCLIP) curViewClip[1] = MINCLIP;
  422.  
  423.     /*Get information about the eye*/
  424.     GetObserverLocation(eyePosn, observer);
  425.     GetFocusPoint(focusPoint, observer);
  426.     GetAdjustedVectors(forwardVector, upVector, observer);
  427.  
  428.     if (ISSTEREO(whichView))
  429.     {
  430.     real sideVector[3];
  431.     real temp;
  432.  
  433.     /*Have to slide eyePosn and focusPoint over somewhat*/
  434.     CROSS(forwardVector, upVector, sideVector);
  435.  
  436.     /*Move eye over*/
  437.     eyePosn[0] += sideVector[0] * eyeOffset;
  438.     eyePosn[1] += sideVector[1] * eyeOffset;
  439.     eyePosn[2] += sideVector[2] * eyeOffset;
  440.     }
  441.  
  442.     /*Get information about the clock*/
  443.     MakeVar(space, TIME);
  444.     var = GetVar(space, TIME);
  445.     if (var)
  446.     {
  447.     spaceTime = GetReal(var);
  448.     }
  449.     else
  450.     {
  451.     spaceTime = 0.0;
  452.     }
  453.  
  454.     /*Set the current space*/
  455.     curSpace = space;
  456.  
  457.     if (action == PICKSPACE)
  458.     {
  459.     StartPick();
  460.     }
  461. #ifdef GL4D
  462.     mmode(MPROJECTION);
  463. #endif
  464.  
  465.     if (whichView & VIEW_MOD_ORTHO)
  466.     {
  467.     /*Plain old orthographic projection.*/
  468.     ortho(-aspect * curViewField, aspect * curViewField,
  469.           -curViewField, curViewField, curViewClip[0], curViewClip[1]);
  470.     }
  471.     else
  472.     {
  473.     /*Exciting perspective projection!!!*/
  474.     perspective((long) (curViewField * 10.0), aspect,
  475.             curViewClip[0], curViewClip[1]);
  476.     }
  477.  
  478. #ifdef GL4D
  479.     mmode(MVIEWING);
  480.     loadmatrix(Identity);
  481. #endif
  482.  
  483. #if 1
  484.     upat(eyePosn[0], eyePosn[1], eyePosn[2],
  485.     focusPoint[0], focusPoint[1], focusPoint[2], 
  486.     upVector[0], upVector[1], upVector[2]);
  487. #else
  488.     lookat(eyePosn[0], eyePosn[1], eyePosn[2],
  489.     focusPoint[0], focusPoint[1], focusPoint[2], 
  490.     0.0);
  491. #endif
  492.  
  493.     getmatrix(viewerCoordsMatrix);
  494.  
  495.     /*Set up the lights*/
  496.     curSpaceLights = GetVar(space, LIGHTS);
  497.     if (action == DRAWSPACE)
  498.     {
  499.     if (curSpaceLights)
  500.     {
  501.         StartLights(space, curSpaceLights, false);
  502.     }
  503.     }
  504.  
  505.     if (action == DRAWSPACE)
  506.     {
  507.     int i;
  508.     real radius;
  509.  
  510.     /*Shift filter for 3-D glasses*/
  511.     if (rgbp && (VIEWOF(whichView) == VIEW_CRLEFT || VIEWOF(whichView) == VIEW_RCRIGHT))
  512.     {
  513.         BeginMask(false, true, true, true);
  514.     }
  515.     else if (rgbp && (VIEWOF(whichView) == VIEW_CRRIGHT || VIEWOF(whichView) == VIEW_RCLEFT))
  516.     {
  517.         BeginMask(true, false, false, true);
  518.     }
  519.     else if (rgbp && (VIEWOF(whichView) == VIEW_GRLEFT || VIEWOF(whichView) == VIEW_RGRIGHT))
  520.     {
  521.         BeginMask(false, true, false, true);
  522.     }
  523.     else if (rgbp && (VIEWOF(whichView) == VIEW_GRRIGHT || VIEWOF(whichView) == VIEW_RGLEFT))
  524.     {
  525.         BeginMask(true, false, false, true);
  526.     }
  527.     else
  528.     {
  529.         BeginMask(true, true, true, true);
  530.     }
  531.  
  532.     if (VIEWOF(whichView) == VIEW_EOLEFT || VIEWOF(whichView) == VIEW_OERIGHT)
  533.     {
  534.         BeginEvenOnly();
  535.     }
  536.     if (VIEWOF(whichView) == VIEW_OELEFT || VIEWOF(whichView) == VIEW_EORIGHT)
  537.     {
  538.         BeginOddOnly();
  539.     }
  540.  
  541.     /*Go to Gouraud shading*/
  542.     shademodel(GOURAUD);
  543.  
  544.     /*Set up the Z-buffer*/
  545.     if (action == DRAWSPACE)
  546.     {
  547.         curZMin = zMin + NUDGESIZE * MAXNNUDGES;
  548.         curZMax = zMax - NUDGESIZE * MAXNNUDGES;
  549.  
  550.         lsetdepth(curZMin, curZMax);
  551.         zbuffer(TRUE);
  552.         zclear();
  553.     }
  554.  
  555.     /*Draw rotation and motion guides*/
  556.     if (whichView & VIEW_MOD_ORTHO)
  557.     {
  558.         radius = SPACESPHERESIZE * 1.85 * curViewField;
  559.     }
  560.     else
  561.     {
  562.         radius = SPACESPHERESIZE * curFocusDist * rsin(curViewField * M_PI / 180.0);
  563.     }
  564.  
  565.     if (GetPredicate(curSpace, SHOWROTGUIDES))
  566.     {
  567.         SetUIColor(UIWHITE);
  568.  
  569.         DrawWFSphere(focusPoint[0], focusPoint[1], focusPoint[2], radius);
  570.         DrawSpaceLine(focusPoint[0] - radius, focusPoint[1], focusPoint[2],
  571.               focusPoint[0] + radius, focusPoint[1], focusPoint[2]);
  572.         DrawSpaceLine(focusPoint[0], focusPoint[1] - radius, focusPoint[2],
  573.               focusPoint[0], focusPoint[1] + radius, focusPoint[2]);
  574.         DrawSpaceLine(focusPoint[0], focusPoint[1], focusPoint[2] - radius,
  575.               focusPoint[0], focusPoint[1], focusPoint[2] + radius);
  576.     }
  577.     if (GetPredicate(curSpace, SHOWMOTGUIDES))
  578.     {
  579.         real x, y;
  580.         SetUIColor(UIGRAY50);
  581.         for (x = -10.0; x <= 10.0; x += 1.0)
  582.         {
  583.         for (y = -10.0; y <= 10.0; y += 1.0)
  584.         {
  585.             DrawSpaceLine(x, y, -10.0, x, y, 10.0);
  586.             DrawSpaceLine(x, -10.0, y, x, 10.0, y);
  587.             DrawSpaceLine(-10.0, x, y, 10.0, x, y);
  588.         }
  589.         }
  590.     }
  591.     }
  592.  
  593.     {
  594.     if (GetPredicate(observer, AUTOADJUST))
  595.     {
  596.         /*Determine the size of the objects within*/ 
  597.         contents = GetVar(space, CONTENTS);
  598.         GetListExtent(contents, bigBounds);
  599.  
  600.         /*Get their center*/
  601.         center[0] = (bigBounds[1] + bigBounds[0]) / 2.0;
  602.         center[1] = (bigBounds[3] + bigBounds[2]) / 2.0;
  603.         center[2] = (bigBounds[5] + bigBounds[4]) / 2.0;
  604.  
  605.         /*Scale to a reasonable scaling factor for the data*/
  606.         maxSize = ABS(bigBounds[1] - bigBounds[0]);
  607.         if (ABS(bigBounds[3] - bigBounds[2]) > maxSize)
  608.         {
  609.         maxSize = ABS(bigBounds[3] - bigBounds[2]);
  610.         }
  611.         if (ABS(bigBounds[5] - bigBounds[4]) > maxSize)
  612.         {
  613.         maxSize = ABS(bigBounds[5] - bigBounds[4]);
  614.         }
  615.         scaleFactor = 1.6 / maxSize;
  616.         SetVar(observer, SPACESCALE, NewReal(1.0 / scaleFactor));
  617.  
  618.         var = NewRealArray(1, 3L);
  619.         CArray2Array(var, center);
  620.         SetVar(observer, SPACEORIGIN, var);
  621.     }
  622.     else
  623.     {
  624.         var = GetRealVar("BeginSpace", observer, SPACESCALE);
  625.         if (var)
  626.         {
  627.         scaleFactor = 1.0 / GetReal(var);
  628.         }
  629.         else
  630.         {
  631.         scaleFactor = 1.0;
  632.         }
  633.  
  634.         var = GetFixedArrayVar("BeginSpace", observer, SPACEORIGIN, 1, 3L);
  635.         if (var)
  636.         {
  637.         Array2CArray(center, var);
  638.         }
  639.         else
  640.         {
  641.         center[0] = 0.0;
  642.         center[1] = 0.0;
  643.         center[2] = 0.0;
  644.         }
  645.     }
  646.  
  647.     /*Save observer position*/
  648.         observerPosition[0] = eyePosn[0] * scaleFactor + center[0];
  649.         observerPosition[1] = eyePosn[1] * scaleFactor + center[1];
  650.         observerPosition[2] = eyePosn[2] * scaleFactor + center[2];
  651.  
  652.     scale(scaleFactor, scaleFactor, scaleFactor);
  653.     translate(-center[0], -center[1], -center[2]);
  654.     }
  655. #endif
  656. }
  657.  
  658. #ifdef PROTO
  659. void StopSpace(int action)
  660. #else
  661. void StopSpace(action)
  662. int action;
  663. #endif
  664. /*Stops drawing, rotating, or pressing in a space*/
  665. {
  666. #ifdef GRAPHICS
  667.     if (action == DRAWSPACE)
  668.     {
  669.     real radius;
  670.  
  671.     if (hasDithering)
  672.     {
  673. #ifdef     DT_OFF
  674.         dither(DT_OFF);
  675. #endif
  676.     }
  677.     if (curWhichView & VIEW_MOD_ORTHO)
  678.     {
  679.         radius = SPACESPHERESIZE * 1.85 * curViewField;
  680.     }
  681.     else
  682.     {
  683.         radius = SPACESPHERESIZE * curFocusDist * rsin(curViewField * M_PI / 180.0);
  684.     }
  685.  
  686.     loadmatrix(viewerCoordsMatrix);
  687.     /*Draw the observer*/
  688.     {
  689.         ObjPtr observer;
  690.         FuncTyp method;
  691.  
  692.         observer = GetVar(curSpace, OBSERVER);
  693.         if (observer)
  694.         {
  695.         method = GetMethod(observer, DRAW);
  696.         if (method)
  697.         {
  698.             (*method)(observer);
  699.         }
  700.         }
  701.     }
  702.     /*Draw the lights*/
  703.     if (curSpaceLights)
  704.     {
  705.         DrawLights(curSpace, curSpaceLights, action, radius);
  706.         StopLights(curSpace);
  707.     }
  708.     EndMask();
  709.     if (VIEWOF(curWhichView) == VIEW_EOLEFT || VIEWOF(curWhichView) == VIEW_OERIGHT)
  710.     {
  711.         EndEvenOnly();
  712.     }
  713.     if (VIEWOF(curWhichView) == VIEW_OELEFT || VIEWOF(curWhichView) == VIEW_EORIGHT)
  714.     {
  715.         EndOddOnly();
  716.     }
  717.  
  718.     }
  719.  
  720.     if (action == PICKSPACE)
  721.     {
  722.     spacePickedObject = StopPick();
  723.     }
  724.     if (action == DRAWSPACE)
  725.     {
  726.     zbuffer(FALSE);
  727.     }
  728.  
  729.     RestoreSubPort();
  730.  
  731. #endif
  732.     curSpace = NULLOBJ;
  733. }
  734.  
  735. ObjPtr DrawObserver(observer)
  736. ObjPtr observer;
  737. /*Draws an observer*/
  738. {
  739.     ObjPtr keyList, result;
  740.  
  741.     if (drawingQuality < DQ_FULL) return ObjTrue;
  742.  
  743.     keyList = NewList();
  744.     PostfixList(keyList, NewSymbol(CLASSID));
  745.     PostfixList(keyList, NewInt(CLASS_SEQUENCE));
  746.  
  747.     zbuffer(FALSE);
  748.     SetUIFont(UIBOLDNORMALFONT);
  749.     SetUIColor(UIWHITE);
  750.  
  751.     result = SearchDatabase(keyList);
  752.     if (result && LISTOF(result))
  753.     {
  754.     ThingListPtr runner;
  755.     char *name;
  756.     
  757.     for (runner = LISTOF(result); runner; runner = runner -> next)
  758.     {
  759.         ObjPtr track, snapshot;
  760.         real time;
  761.         real frameWidth;
  762.         int whichFrame;
  763.         ObjPtr var;
  764.         track = GetTrackedVarGroupWithinSequence(runner -> thing, observer, "Placement");
  765.         if (track)
  766.         {
  767.         ObjPtr var;
  768.         ObjPtr snap;
  769.         ObjPtr *joints;
  770.         ObjPtr *keyframes;
  771.         int k;
  772.         int nJoints;
  773.         int nKeyframes;
  774.         real loc[3];
  775.         real vw[3];
  776.         real l1[3], l2[3];
  777.         int botFrame, topFrame;
  778.         Quaternion qrot;    /*Quaternion for rotation*/
  779.  
  780.         var = GetVar(track, KEYFRAMES);
  781.         if (!var) continue;
  782.         nKeyframes = DIMS(var)[0];
  783.         keyframes = ELEMENTS(var);
  784.  
  785.         for (k = 0; k < nKeyframes; ++k)
  786.         {
  787.             char str[20];
  788.  
  789.             snap = GetVar(keyframes[k], SNAPSHOT);
  790.  
  791.             var = GetVar(snap, LOCATION);
  792.             Array2CArray(loc, var);
  793.  
  794.             sprintf(str, "%d", k);
  795.             DrawSpaceString(loc[0], loc[1], loc[2],
  796.             str);
  797.  
  798.             var = GetVar(keyframes[k], WORLDVELOCITY);
  799.             if (var)
  800.             {
  801.             Array2CArray(vw, var);
  802.  
  803.             DrawSpaceLine(loc[0], loc[1], loc[2],
  804.                 loc[0] + vw[0] * 5.0,
  805.                 loc[1] + vw[1] * 5.0,
  806.                 loc[2] + vw[2] * 5.0);
  807.             }
  808.         }
  809.  
  810.         /*Get the joints*/
  811.         MakeVar(track, JOINTS);
  812.         var = GetVar(track, JOINTS);
  813.         if (!var) continue;
  814.         nJoints = DIMS(var)[0];
  815.         joints = ELEMENTS(var);
  816.  
  817.         /*Find the range of keyframes*/
  818.         var = GetIntVar("DrawObserver", keyframes[0], WHICHFRAME);
  819.         if (!var) return ObjTrue;
  820.         botFrame = GetInt(var);
  821.  
  822.         var = GetIntVar("DrawObserver", keyframes[nKeyframes - 1], WHICHFRAME);
  823.         if (!var) return ObjTrue;
  824.         topFrame = GetInt(var);
  825.  
  826.         if (topFrame > botFrame)
  827.         {
  828.             TweenObserverPlacement(track, botFrame, qrot, l1);
  829.             for (k = 0; k < topFrame; ++k)
  830.             {
  831.             TweenObserverPlacement(track, k + 1, qrot, l2);
  832.  
  833.             SetUIColor(k & 1 ? UIWHITE : UICYAN);
  834.             DrawSpaceLine(l1[0], l1[1], l1[2], l2[0], l2[1], l2[2]);
  835.  
  836.             l1[0] = l2[0];
  837.             l1[1] = l2[1];
  838.             l1[2] = l2[2];
  839.             }
  840.         }
  841.          }
  842.     }
  843.     }
  844.     return ObjTrue;
  845. }
  846.  
  847. static ObjPtr ForAllSpaceObjects(object, routine)
  848. ObjPtr object;
  849. FuncTyp routine;
  850. /*Does routine on a space and its contents*/
  851. {
  852.     ObjPtr contents;
  853.     ObjPtr lights;
  854.  
  855.     (*routine)(object);
  856.  
  857.     /*Now check my CONTENTS*/
  858.     contents = GetVar(object, CONTENTS);
  859.     if (contents)
  860.     {
  861.     ForAllObjects(contents, routine);
  862.     }
  863.  
  864.     /*Now check the lights*/
  865.     lights = GetVar(object, LIGHTS);
  866.     if (lights)
  867.     {
  868.     ForAllObjects(lights, routine);
  869.     }
  870.  
  871.     /*DIK also observer and renderer and clock*/
  872.  
  873.     return ObjTrue;
  874. }
  875.  
  876.  
  877. ObjPtr WakeObserver(observer, lateness)
  878. ObjPtr observer;
  879. double lateness;
  880. /*Wakes an observer and makes it continue rotation*/
  881. {
  882.     ObjPtr var;
  883.     Bool wakeAgain = false;    /*True iff wake again*/
  884.     int x, y;
  885.     real phi;
  886.  
  887.     DoNotDisturb(observer, MARKTIME);
  888.  
  889.     /*See if we need to spin*/
  890.     var = GetVar(observer, ROTSPEED);
  891.     if (var && 0.0 != (phi = GetReal(var)))
  892.     {
  893.     real axis[3];
  894.  
  895.     /*Turn phi from speed to amount*/
  896.     phi *= lateness;
  897.  
  898.     var = GetFixedArrayVar("WakeObserver", observer, ROTAXIS, 1, 3L);
  899.     if (var)
  900.     {
  901.         Array2CArray(axis, var);
  902.  
  903.         RotateObserver(observer, axis, phi, false);
  904.     }
  905.  
  906.     wakeAgain = true;
  907.     }
  908.     /*See if we want to fly*/
  909.     else if (GetPredicate(observer, FLYING))
  910.     {
  911. #ifdef INTERACTIVE
  912.     ObjPtr var;
  913.     real eyePosn[3];
  914.     real roll, dPitch, pitch;
  915.     float dTime;
  916.     real perspecStuff[4];
  917.     real airspeed;
  918.     long mouseX, mouseY;
  919.     real upVector[3];
  920.     real forwardVector[3];
  921.     real sp, cp, t;
  922.     real temp[3];
  923.  
  924.     dTime = lateness;
  925.  
  926.     /*Move eye according to airspeed*/
  927.     var = GetVar(observer, AIRSPEED);
  928.     if (var && IsReal(var))
  929.     {
  930.         airspeed = GetReal(var);
  931.     }
  932.     else
  933.     {
  934.         airspeed = 0.0;
  935.     }
  936.     
  937.     var = GetVar(observer, LOCATION);
  938.     if (var && IsRealArray(var) && RANK(var) == 1 && 
  939.         DIMS(var)[0] == 3)
  940.     {
  941.         Array2CArray(eyePosn, var);
  942.     }
  943.     else
  944.     {
  945.         eyePosn[0] = 0.0;
  946.         eyePosn[1] = 0.0;
  947.         eyePosn[2] = INITEYEDIST;
  948.         var = NewRealArray(1, (long) 3);
  949.         SetVar(observer, LOCATION, var);
  950.     }
  951.  
  952.     /*Get a new roll and dpitch value*/
  953.     mouseX = getvaluator(MOUSEX);
  954.     mouseY = getvaluator(MOUSEY);
  955.  
  956.     roll = (((real) mouseX) / (real) scrWidth) * 2 * MAXROLL - MAXROLL;
  957.     dPitch = (((real) mouseY) / (real) scrHeight) * 2 * MAXDPITCH - MAXDPITCH;
  958.  
  959.     /*Update eye position based on airspeed*/
  960.  
  961.     /*Get pitch*/
  962.     var = GetVar(observer, PITCH);
  963.     if (var && IsReal(var))
  964.     {
  965.         pitch = GetReal(var);
  966.     }
  967.     else
  968.     {
  969.         pitch = 0.0;
  970.         var = NewReal(pitch);
  971.         SetVar(observer, PITCH, var);
  972.     }
  973.     
  974.     /*Change the pitch based on dpitch, asymptotic to up and down*/
  975.     if (dPitch > 0.0)
  976.     {
  977.         pitch = pitch + (M_PI_2 - pitch) * dPitch * dTime;
  978.     }
  979.     else
  980.     {
  981.         pitch = pitch + (pitch + M_PI_2) * dPitch * dTime;
  982.     }
  983.     SetVar(observer, PITCH, NewReal(pitch));
  984.  
  985.     /*Rotate around the up vector*/
  986.     phi = roll * ROLLYAWFACTOR * dTime;
  987.     GetUpVector(upVector, observer);
  988.     RotateObserver(observer, upVector, phi, false);
  989.     GetAdjustedVectors(forwardVector, upVector, observer);
  990.  
  991.     /*Move eye*/
  992.     eyePosn[0] += forwardVector[0] * airspeed * dTime;
  993.     eyePosn[1] += forwardVector[1] * airspeed * dTime;
  994.     eyePosn[2] += forwardVector[2] * airspeed * dTime; 
  995.  
  996.     var = NewRealArray(1, 3L);
  997.     CArray2Array(var, eyePosn);
  998.     SetVar(observer, LOCATION, var);
  999.     
  1000.     ImInvalid(observer);
  1001.     wakeAgain = true;
  1002. #endif
  1003.     }
  1004.     if (wakeAgain)
  1005.     {
  1006.     WakeMe(observer, MARKTIME, Clock() + 0.001);
  1007.     }
  1008.     return ObjTrue;
  1009. }
  1010.  
  1011. #ifdef PROTO
  1012. void SetRotationMotor(real rotSpeed, real ax, real ay, real az)
  1013. #else
  1014. void SetRotationMotor(rotSpeed, ax, ay, az)
  1015. real rotSpeed, ax, ay, az;
  1016. #endif
  1017. /*Sets a rotation motor at rotSpeed around axis ax, ay, az*/
  1018. {
  1019.     ObjPtr var;
  1020.     real axis[3];
  1021.     char logLine[256];
  1022.     ObjPtr space, observer;
  1023.  
  1024.     if (!selWinInfo)
  1025.     {
  1026.     return;
  1027.     }
  1028.  
  1029.     space = FindSpace(selWinInfo);
  1030.     if (!space)
  1031.     {
  1032.     return;
  1033.     }
  1034.  
  1035.     observer = GetObjectVar("SetRotationMotor", space, OBSERVER);
  1036.     if (!observer)
  1037.     {
  1038.     return;
  1039.     }
  1040.  
  1041.     var = NewRealArray(1, 3L);
  1042.     axis[0] = ax;
  1043.     axis[1] = ay;
  1044.     axis[2] = az;
  1045.     CArray2Array(var, axis);
  1046.  
  1047.     SetVar(observer, ROTAXIS, var);
  1048.     SetVar(observer, ROTSPEED, NewReal(rotSpeed));
  1049.  
  1050.     if (logging)
  1051.     {
  1052.     sprintf(logLine, "set rotation %g [%g %g %g]\n",
  1053.         rotSpeed * 180.0 / M_PI, ax, ay, az);
  1054.     Log(logLine);
  1055.     }
  1056.  
  1057.     if (rotSpeed > 0.0)
  1058.     {
  1059.     DoNotDisturb(observer, MARKTIME);
  1060.     WakeMe(observer, MARKTIME, Clock() + 0.001);
  1061.     }
  1062. }
  1063.  
  1064. #ifdef PROTO
  1065. void RecordObserver(ObjPtr observer)
  1066. #else
  1067. void RecordObserver(observer)
  1068. ObjPtr observer;
  1069. #endif
  1070. /*Records the current status of an observer*/
  1071. {
  1072.     ObjPtr keyList, result;
  1073.  
  1074.     keyList = NewList();
  1075.     PostfixList(keyList, NewSymbol(CLASSID));
  1076.     PostfixList(keyList, NewInt(CLASS_SEQUENCE));
  1077.     PostfixList(keyList, NewSymbol(RECORDING));
  1078.     PostfixList(keyList, ObjTrue);
  1079.  
  1080.     result = SearchDatabase(keyList);
  1081.     if (result && LISTOF(result))
  1082.     {
  1083.     ThingListPtr runner;
  1084.     char *name;
  1085.  
  1086.     runner = LISTOF(result);
  1087.     while (runner)
  1088.     {
  1089.         ObjPtr track, snapshot;
  1090.         real time;
  1091.         real frameWidth;
  1092.         int whichFrame;
  1093.         ObjPtr var;
  1094.         track = TrackVarGroupWithinSequence(runner -> thing, observer, "Placement", placementTrackClass);
  1095.         if (track)
  1096.         {
  1097.         ObjPtr list;
  1098.  
  1099.         MakeVar(runner -> thing, FRAMERATE);
  1100.         var = GetVar(runner -> thing, FRAMERATE);
  1101.         if (var)
  1102.         {
  1103.             frameWidth = 1.0 / GetReal(var);
  1104.         }
  1105.         else
  1106.         {
  1107.             frameWidth = 1.0 / 30.0;
  1108.         }
  1109.  
  1110.         var = GetVar(runner -> thing, TIME);
  1111.         if (var)
  1112.         {
  1113.             time = GetReal(var);
  1114.         }
  1115.         else
  1116.         {
  1117.             time = 0.0;
  1118.         }
  1119.  
  1120.         whichFrame = (int) floor(time / frameWidth + 0.5);
  1121.         
  1122.         list = NewList();
  1123.         PostfixList(list, NewSymbol(LOCATION));
  1124.         PostfixList(list, NewSymbol(ROTQUAT));
  1125.         PostfixList(list, NewSymbol(FOCUSDIST));
  1126.         snapshot = TakeVarsSnapshot(observer, list);
  1127.         InsertKeySnapshot(track, whichFrame, snapshot);
  1128.         SetVar(track, CHANGED, ObjTrue);
  1129.         }
  1130.         runner = runner -> next;
  1131.     }
  1132.     }
  1133. }
  1134.  
  1135. #ifdef PROTO
  1136. void LogObserver(ObjPtr observer)
  1137. #else
  1138. void LogObserver(observer)
  1139. ObjPtr observer;
  1140. #endif
  1141. /*Logs an observer*/
  1142. {
  1143.     ObjPtr var;
  1144.     real temp[3];
  1145.  
  1146.     RecordObserver(observer);
  1147.  
  1148.     if (logging)
  1149.     {
  1150.     MakeObjectName(tempStr, observer);
  1151.     Log("begin snapshot ");
  1152.     Log(tempStr);
  1153.     Log("\n");
  1154.     LogVariable(observer, LOCATION);
  1155.     LogVariable(observer, ROTQUAT);
  1156.     LogVariable(observer, FOCUSDIST);
  1157.     Log("end snapshot\n");
  1158.     }
  1159. }
  1160.  
  1161. #ifdef PROTO
  1162. ObjPtr MoveSpace(ObjPtr object, int x, int y, long flags)
  1163. #else
  1164. ObjPtr MoveSpace(object, x, y, flags)
  1165. ObjPtr object;
  1166. int x, y;
  1167. long flags;
  1168. #endif
  1169. /*Does a move in a space beginning at x and y.  Returns
  1170.   ObjTrue if it was a good move.*/
  1171. {
  1172. #ifdef INTERACTIVE
  1173.     int d;
  1174.     int newX, newY;
  1175.     ObjPtr objects, observer, corral;
  1176.     ThingListPtr runner;
  1177.     ObjPtr tempObj;
  1178.     real eyePosn[3];
  1179.     real up[3], forward[3], side[3];
  1180.     real moveVector[3];
  1181.     real uMove, vMove;
  1182.     real sr, cr;
  1183.     real sp, cp;
  1184.     real sy, cy;
  1185.     real sf;
  1186.     real focusDist;
  1187.     int left, right, bottom, top;
  1188.  
  1189.     Get2DIntBounds(object, &left, &right, &bottom, &top);
  1190.  
  1191.     d = top - bottom;
  1192.  
  1193.     corral = GetVar(object, CORRAL);
  1194.  
  1195.     SetRotationMotor(0.0, 0.0, 0.0, 1.0);
  1196.  
  1197.     SetVar(object, MOVING, ObjTrue);
  1198.  
  1199.     /*Get info about the observer*/
  1200.     observer = GetObjectVar("MoveSpace", object, OBSERVER);
  1201.  
  1202.     SaveForUndo(observer);
  1203.  
  1204.     GetObserverLocation(eyePosn, observer);
  1205.     GetAdjustedVectors(forward, up, observer);
  1206.     CROSS(forward, up, side);
  1207.  
  1208.     /*Get perspective stuff for scaling movement and eyeball distance*/
  1209.     tempObj = GetRealVar("MoveSpace", observer, FOCUSDIST);
  1210.     if (tempObj)
  1211.     {
  1212.     focusDist = GetReal(tempObj);
  1213.     }
  1214.     else
  1215.     {
  1216.     focusDist = INITEYEDIST;
  1217.     }
  1218.  
  1219.     if (flags & F_DOUBLECLICK)
  1220.     {
  1221.     
  1222.     }
  1223.     /*Press the contents of the space*/
  1224.     else while (Mouse(&newX, &newY))
  1225.     {
  1226.     if ((newX != x) || (newY != y))
  1227.     {
  1228.         /*Get raw uMove and vMove*/
  1229.         sf = focusDist * MOVEFACTOR;
  1230.         uMove = sf * ((float) (x - newX)) / (float) d;
  1231.         vMove = sf * ((float) (y - newY)) / (float) d;
  1232.  
  1233.         moveVector[0] = uMove * side[0] + vMove * up[0];
  1234.  
  1235.         /*Produce a motion vector*/
  1236.         moveVector[0] = uMove * side[0] + vMove * up[0];
  1237.         moveVector[1] = uMove * side[1] + vMove * up[1];
  1238.         moveVector[2] = uMove * side[2] + vMove * up[2];
  1239.  
  1240.         if (flags & F_SHIFTDOWN)
  1241.         {
  1242.         int k, best = 0;
  1243.         float curDot, maxDot = 0.0;
  1244.         float rr;
  1245.  
  1246.         /*Constrain the motion axis to the nearest ortho axis*/
  1247.         for (k = 0; k < 3; ++k)
  1248.         {
  1249.             curDot =
  1250.             moveVector[0] * Identity[k][0] +
  1251.             moveVector[1] * Identity[k][1] +
  1252.             moveVector[2] * Identity[k][2];
  1253.  
  1254.             if (ABS(curDot) > ABS(maxDot))
  1255.             {
  1256.             /*It's a better choice*/
  1257.             maxDot = curDot;
  1258.             best = k;
  1259.             }
  1260.         }
  1261.  
  1262.         /*Now we have a best match*/
  1263.         moveVector[0] = maxDot * Identity[best][0];
  1264.         moveVector[1] = maxDot * Identity[best][1];
  1265.         moveVector[2] = maxDot * Identity[best][2];
  1266.         }
  1267.  
  1268.         eyePosn[0] += moveVector[0];
  1269.         eyePosn[1] += moveVector[1];
  1270.         eyePosn[2] += moveVector[2];
  1271.  
  1272.         /*Put eyePosn back*/
  1273.         tempObj = NewRealArray(1, 3L);
  1274.         CArray2Array(tempObj, eyePosn);
  1275.         SetVar(observer, LOCATION, tempObj);
  1276.  
  1277.         x = newX;
  1278.         y = newY;
  1279.  
  1280.         ImInvalid(observer);
  1281.         DrawMe(object);
  1282.     }
  1283.     }
  1284.     SetVar(object, MOVING, ObjFalse);
  1285.  
  1286.     if (logging)
  1287.     {
  1288.     LogObserver(observer);
  1289.     }
  1290. #endif
  1291.     return ObjTrue;
  1292. }
  1293.  
  1294. #ifdef PROTO
  1295. ObjPtr RotateSpace(ObjPtr object, int x, int y, long flags)
  1296. #else
  1297. ObjPtr RotateSpace(object, x, y, flags)
  1298. ObjPtr object;
  1299. int x, y;
  1300. long flags;
  1301. #endif
  1302. /*Does a rotate in a space beginning at x and y.  Returns
  1303.   true iff the rotate really was in the panel.*/
  1304. {
  1305.     int left, right, bottom, top;
  1306.     ObjPtr observer/*, observers*/;
  1307.     ObjPtr lights;
  1308.     ThingListPtr runner;
  1309.  
  1310. #ifdef INTERACTIVE
  1311.     Get2DIntBounds(object, &left, &right, &bottom, &top);
  1312.  
  1313.     /*See if there are any lights to rotate*/
  1314.     lights = GetVar(object, LIGHTS);
  1315.     if (lights)
  1316.     {
  1317.     runner = LISTOF(lights);
  1318.     while (runner)
  1319.     {
  1320.         if (IsSelected(runner -> thing))
  1321.         {
  1322.         break;
  1323.         }
  1324.         runner = runner -> next;
  1325.     }
  1326.     }
  1327.  
  1328.     /*If none selected, say there are none*/
  1329.     if (!runner)
  1330.     {
  1331.     lights = NULLOBJ;
  1332.     }
  1333.  
  1334.     /*Get the observer*/
  1335.     observer = GetObjectVar("RotateSpace", object, OBSERVER);
  1336.  
  1337.     if (observer)
  1338.     {
  1339.     Bool motorOn = false;        /*True iff motor on*/
  1340.     float rotSpeed = 0.0;        /*Rotation speed*/
  1341.     float ax, ay, az;            /*Three components of axis of rotation*/
  1342.     real axis[3];            /*Final axis of rotation*/
  1343.     ObjPtr var;                /*Temporary var*/
  1344.     real eyePosn[3];    /*Observer info*/
  1345.     real focusDist;
  1346.     real focusPoint[3];
  1347.     real forward[3], up[3], side[3];    /*Observer axes*/
  1348.  
  1349.     var = GetRealVar("RotateSpace", observer, FOCUSDIST);
  1350.     if (var)
  1351.     {
  1352.         focusDist = GetReal(var);
  1353.     }
  1354.     else
  1355.     {
  1356.         focusDist = INITEYEDIST;
  1357.     }
  1358.     GetFocusPoint(focusPoint, observer);
  1359.     GetObserverLocation(eyePosn, observer);
  1360.     GetAdjustedVectors(forward, up, observer);
  1361.  
  1362.     CROSS(forward, up, side);
  1363.     NORMALIZE(side);
  1364.  
  1365.     if (flags & F_DOUBLECLICK)
  1366.     {
  1367.         if (lights)
  1368.         {
  1369.         /*Double click.  Snap lights to their closest values*/
  1370.  
  1371.         runner = LISTOF(lights);
  1372.         while (runner)
  1373.         {
  1374.             if (IsSelected(runner -> thing))
  1375.             {
  1376.             /*Rotate the light*/
  1377.             ObjPtr var;
  1378.             int i;
  1379.             float biggest;
  1380.             int ibiggest;
  1381.             real location[3];
  1382.     
  1383.             var = GetFixedArrayVar("RotateSpace", runner -> thing, LOCATION, 1, 3L);
  1384.             if (var)
  1385.             {
  1386.                 Array2CArray(location, var);
  1387.                 biggest = 0.0;
  1388.                 ibiggest = 0;
  1389.                 for (i = 0; i < 3; ++i)
  1390.                 {
  1391.                 if (ABS(location[i]) > biggest)
  1392.                 {
  1393.                     ibiggest = i;
  1394.                     biggest = ABS(location[i]);
  1395.                 }
  1396.                 }
  1397.                 location[ibiggest] =
  1398.                 location[ibiggest] > 0.0 ? 1.0 : -1.0;
  1399.                 for (i = 0; i < 3; ++i)
  1400.                 {
  1401.                 if (i != ibiggest)
  1402.                 {
  1403.                     location[i] = 0.0;
  1404.                 }
  1405.                 }
  1406.                 var = NewRealArray(1, 3L);
  1407.                 CArray2Array(var, location);
  1408.                 SetVar(runner -> thing, LOCATION, var);
  1409.                 ImInvalid(runner -> thing);
  1410.             }
  1411.             }
  1412.             runner = runner -> next;
  1413.         }
  1414.         }
  1415.         else
  1416.         {
  1417.         /*It was a double click.  Align forward and up to the
  1418.           nearest orthogonal unit vector*/
  1419.         real newRot[3][3];    /*New rotation matrix*/
  1420.         Quaternion rotQuat;    /*New quaternion rotation matrix*/
  1421.         Bool set;        /*True iff this j has been set nonzero*/
  1422.         int i, j;        /*Counters within the matrix*/
  1423.         float biggest;    /*The biggest xform coefficient so far*/
  1424.         int jbiggest;    /*The j of biggest*/
  1425.  
  1426.         SaveForUndo(observer);
  1427.         /*First do forward*/
  1428.         biggest = 0.0;
  1429.         jbiggest = -1;
  1430.         for (j = 0; j < 3; ++j)
  1431.         {
  1432.             if (ABS(forward[j]) > biggest)
  1433.             {
  1434.             biggest = ABS(forward[j]);
  1435.             jbiggest = j;
  1436.             }
  1437.         }
  1438.         if (jbiggest == -1)
  1439.         {
  1440.             return ObjTrue;
  1441.         }
  1442.         for (j = 0; j < 3; ++j)
  1443.         {
  1444.             if (j == jbiggest)
  1445.             {
  1446.             forward[j] = forward[j] > 0 ? 1.0 : -1.0;
  1447.             }
  1448.             else
  1449.             {
  1450.             forward[j] = 0.0;
  1451.             }
  1452.         }
  1453.  
  1454.         /*Now do up*/
  1455.         biggest = 0.0;
  1456.         jbiggest = -1;
  1457.         for (j = 0; j < 3; ++j)
  1458.         {
  1459.             if (ABS(up[j]) > biggest)
  1460.             {
  1461.             biggest = ABS(up[j]);
  1462.             jbiggest = j;
  1463.             }
  1464.         }
  1465.         if (jbiggest == -1)
  1466.         {
  1467.             return ObjTrue;
  1468.         }
  1469.         for (j = 0; j < 3; ++j)
  1470.         {
  1471.             if (j == jbiggest)
  1472.             {
  1473.             up[j] = up[j] > 0 ? 1.0 : -1.0;
  1474.             }
  1475.             else
  1476.             {
  1477.             up[j] = 0.0;
  1478.             }
  1479.         }
  1480.  
  1481.         /*Make sure up is not the same as forward*/
  1482.         if ((ABS(forward[0]) == ABS(up[0])) &&
  1483.             (ABS(forward[1]) == ABS(up[1])) &&
  1484.             (ABS(forward[2]) == ABS(up[2])))
  1485.         {
  1486.             /*Make a different up based on forward*/
  1487.             up[0] = forward[1];
  1488.             up[1] = forward[2];
  1489.             up[2] = forward[0];
  1490.         }
  1491.  
  1492.         /*Make new variables for observer*/
  1493.         eyePosn[0] = focusPoint[0] - focusDist * forward[0];
  1494.         eyePosn[1] = focusPoint[1] - focusDist * forward[1];
  1495.         eyePosn[2] = focusPoint[2] - focusDist * forward[2];
  1496.         var = NewRealArray(1, 3L);
  1497.         CArray2Array(var, eyePosn);
  1498.         SetVar(observer, LOCATION, var);
  1499.  
  1500.         CROSS(forward, up, side);
  1501.  
  1502.         /*Make a matrix*/
  1503.         newRot[0][0] = side[0];
  1504.         newRot[1][0] = side[1];
  1505.         newRot[2][0] = side[2];
  1506.  
  1507.         newRot[0][1] = up[0];
  1508.         newRot[1][1] = up[1];
  1509.         newRot[2][1] = up[2];
  1510.  
  1511.         newRot[0][2] = -forward[0];
  1512.         newRot[1][2] = -forward[1];
  1513.         newRot[2][2] = -forward[2];
  1514.  
  1515.         MatrixToQuaternion(newRot, rotQuat);
  1516.         var = NewRealArray(1, 4L);
  1517.         CArray2Array(var, rotQuat);
  1518.         SetVar(observer, ROTQUAT, var);
  1519.  
  1520.         ImInvalid(observer);
  1521.         if (logging)
  1522.         {
  1523.             LogObserver(observer);
  1524.         }
  1525.         }
  1526.         ImInvalid(object);
  1527.     }
  1528.     else
  1529.     {
  1530.         /*It's a click and drag.  Do the trackball stuff.*/
  1531.         real Ox, Oy;        /*Center of virtual trackball*/
  1532.         real r;            /*Radius of virtual trackball*/
  1533.         real omega;            /*Ratio of the click radius to the trackball radius*/
  1534.         real theta;            /*Angle to the click radius*/
  1535.         real tao;            /*Angle from the click radius to the direction*/
  1536.         real phi;            /*Amount the trackball is pushed*/
  1537.         int newX, newY;        /*New X and Y values*/
  1538.         Bool firstRun = true;    /*The first run*/
  1539.         real lastTime = 0.0;    /*The last time*/
  1540.         real cumPhi = 0.0;        /*Cumulative phi*/
  1541.         real curTime = 0.0;        /*The current time*/
  1542.         int viewType;        /*The view type*/
  1543.         int stereoMode;        /*The stereo mode*/
  1544.  
  1545.         if (!lights)
  1546.         {
  1547.         SaveForUndo(observer);
  1548.         }
  1549.  
  1550.         /*Get the view type*/
  1551.         var = GetVar(observer, VIEWTYPE);
  1552.         if (var)
  1553.         {
  1554.         viewType = GetInt(var);
  1555.         }
  1556.         else
  1557.         {
  1558.         viewType == VT_PERSPECTIVE;
  1559.         }
  1560.  
  1561.         var = GetVar(observer, STEREOMODE);
  1562.         if (var)
  1563.         {
  1564.         stereoMode = GetInt(var);
  1565.         }
  1566.         else
  1567.         {
  1568.         stereoMode == SM_MONO;
  1569.         }
  1570.  
  1571.         SetVar(object, ROTATING, ObjTrue);
  1572.  
  1573.         /*Calculate origin and radius of trackball*/
  1574.         if (stereoMode == SM_CROSSEYED || viewType == SM_WALLEYED)
  1575.         {
  1576.         if (x < (left + right) / 2)
  1577.         {
  1578.             Ox = (left + right) / 4;
  1579.         }
  1580.         else
  1581.         {
  1582.             Ox = 3 * (left + right) / 4;
  1583.         }
  1584.         Oy = (top + bottom) / 2;
  1585.         }
  1586.         else if (stereoMode == SM_LEFTONTOP)
  1587.         {
  1588.         Ox = (left + right) / 2;
  1589.         if (y < YSTEREO)
  1590.         {
  1591.             Oy = YSTEREO / 2;
  1592.         }
  1593.         else
  1594.         {
  1595.             Oy = YSTEREO * 3 / 2 + YBLANK;
  1596.         }
  1597.         }
  1598.         else if (stereoMode == SM_RIGHTONTOP)
  1599.         {
  1600.         Ox = (left + right) / 2;
  1601.         if (y < YSTEREO)
  1602.         {
  1603.             Oy = YSTEREO / 2;
  1604.         }
  1605.         else
  1606.         {
  1607.             Oy = YSTEREO * 3 / 2 + YBLANK;
  1608.         }
  1609.         }
  1610.         else
  1611.         {
  1612.         Ox = (left + right) / 2;
  1613.         Oy = (top + bottom) / 2;
  1614.         }
  1615.         r = 0.4 * (float) (top - bottom);
  1616.  
  1617.         x -= Ox;        /*Offset x and y around trackball*/
  1618.         y -= Oy;
  1619.  
  1620.         while (Mouse(&newX, &newY))
  1621.         {
  1622.         newX -= Ox;        /*Offset the new x and y*/
  1623.         newY -= Oy;
  1624.         
  1625.         if (ABS(newX - x) >= MINROT || ABS(newY - y) >= MINROT)
  1626.         {
  1627.             /*Now we have a differential*/
  1628.             real dr;            /*Click axis distance*/
  1629.             real sw, cw;        /*Sin and cosine of omega*/
  1630.             real st, ct;        /*Sin and cosine of theta*/
  1631.             real sp, cp;        /*Sin and cosine of phi*/
  1632.             real a1x, a1y, a1z;        /*Temporary values for axis of rotation*/
  1633.             real a2x, a2y, a2z;        /*Temporary values for axis of rotation*/
  1634.             real t;            /*1-cos phi*/
  1635.  
  1636.             dr = rsqrt(SQUARE((float) x) +
  1637.                    SQUARE((float) y));
  1638.             omega = f(dr / r);
  1639.  
  1640.             /*Calculate theta*/
  1641.             theta = ratan2((float) (y), (float) (x));
  1642.  
  1643.             /*Calculate tao as offset from theta*/
  1644.             tao = ratan2((float) (newY - y), (float) (newX - x)) - theta;
  1645.  
  1646.             /*Calculate phi simplistically*/
  1647.             phi = rsqrt(SQUARE((float) (newX - x)) +
  1648.                 SQUARE((float) (newY - y))) / r;
  1649.  
  1650.             /*Calculate sin and cos of the angles for speed*/
  1651.             sw = rsin(omega);
  1652.             cw = rcos(omega);
  1653.             st = rsin(theta);
  1654.             ct = rcos(theta);
  1655.             sp = rsin(phi);
  1656.             cp = rcos(phi);
  1657.             t = 1.0 - cp;
  1658.  
  1659.             /*Calculate the axis of rotation*/
  1660.  
  1661.             /*First the motion from origin component*/
  1662.             a1x = -rsin(tao);
  1663.             a1y = rcos(tao);
  1664.             a1z = 0.0;
  1665.  
  1666.             /*Now multiply in the "on x-axis" component*/
  1667.             a2x = a1x * cw + a1z * sw;
  1668.             a2y = a1y;
  1669.             a2z = a1x * -sw + a1z * cw;
  1670.  
  1671.             /*Now multiply in the "from x-axis" component*/
  1672.             ax = a2x * ct + a2y * -st;
  1673.             ay = a2x * st + a2y * ct;
  1674.             az = a2z;
  1675.  
  1676.             /*Calculate the phi and delta time*/
  1677.             if (firstRun)
  1678.             {
  1679.             firstRun = false;
  1680.             cumPhi = 0.0;
  1681.             lastTime = Clock();
  1682.             }
  1683.             else
  1684.             {
  1685.             curTime = Clock();
  1686.             cumPhi += phi;
  1687.             if (curTime > lastTime + MINROTTIME)
  1688.             {
  1689.                 motorOn = true;
  1690.                 rotSpeed = cumPhi / (curTime - lastTime);
  1691.                 lastTime = curTime;
  1692.                 cumPhi = 0.0;
  1693.             }
  1694.             }
  1695.  
  1696.             /*Calculate axis based on combinations of
  1697.               forward, up, and side vectors*/
  1698.  
  1699.             axis[0] = 0.0;
  1700.             axis[1] = 0.0;
  1701.             axis[2] = 0.0;
  1702.  
  1703.             axis[0] += ax * side[0];
  1704.             axis[1] += ax * side[1];
  1705.             axis[2] += ax * side[2];
  1706.  
  1707.             axis[0] += ay * up[0];
  1708.             axis[1] += ay * up[1];
  1709.             axis[2] += ay * up[2];
  1710.  
  1711.             axis[0] -= az * forward[0];
  1712.             axis[1] -= az * forward[1];
  1713.             axis[2] -= az * forward[2];
  1714.  
  1715.             /*Now that there's a new delta, save it and redraw*/
  1716.             if (lights)
  1717.             {
  1718.             RotateLights(lights, axis, phi, flags & F_SHIFTDOWN ? true : false, observer, object);
  1719.             }
  1720.             else
  1721.             {
  1722.             RotateObserver(observer, axis, phi, flags & F_SHIFTDOWN ? true : false);
  1723.             }
  1724.  
  1725.             x = newX;
  1726.             y = newY;
  1727.             ImInvalid(observer);
  1728.             DrawMe(object);
  1729.         }
  1730.         else
  1731.         {
  1732.             firstRun = true;
  1733.             motorOn = false;
  1734.         }
  1735.         }
  1736.         SetVar(object, ROTATING, false);
  1737.     }
  1738.  
  1739.     if (logging)
  1740.     {
  1741.         char cmd[256];
  1742.         char *s;
  1743.         Bool rotLights;
  1744.         ThingListPtr runner;
  1745.  
  1746.         /*See if it's lights that were rotated*/
  1747.         rotLights = false;
  1748.         if (lights)
  1749.         {
  1750.         runner = LISTOF(lights);
  1751.  
  1752.         while (runner)
  1753.         {
  1754.             if (IsSelected(runner -> thing))
  1755.             {
  1756.             /*Rotated light*/
  1757.         
  1758.             ObjPtr var;
  1759.             real location[3];
  1760.                 
  1761.             rotLights = true;
  1762.             var = GetFixedArrayVar("RotateSpace", runner -> thing, LOCATION, 1, 3L);
  1763.             if (var)
  1764.             {
  1765.                 Array2CArray(location, var);
  1766.                 sprintf(cmd, "set location ");
  1767.                 s = cmd;
  1768.                 while (*s) ++s;
  1769.                 MakeObjectName(s, runner -> thing);
  1770.                 while (*s) ++s;
  1771.                 sprintf(s, " [%g %g %g]\n",
  1772.                     location[0],
  1773.                     location[1],
  1774.                     location[2]);
  1775.                 Log(cmd);
  1776.             }
  1777.             }
  1778.             runner = runner -> next;
  1779.         }
  1780.         }
  1781.         else
  1782.         {
  1783.         /*No lights rotated*/
  1784.         LogObserver(observer);
  1785.         }
  1786.     }
  1787.  
  1788.     if (!lights)
  1789.     {
  1790.         if (GetPrefTruth(PREF_ROTINERTIA) && motorOn)
  1791.         {
  1792.         SetRotationMotor((real) rotSpeed, axis[0], axis[1], axis[2]);
  1793.         }
  1794.         else
  1795.         {
  1796.         SetRotationMotor((real) 0.0, axis[0], axis[1], axis[2]);
  1797.         }
  1798.     }
  1799.  
  1800.     }
  1801. #endif
  1802.     return ObjTrue;
  1803. }
  1804.  
  1805. static ObjPtr FingerSpace(space, x, y, flags)
  1806. ObjPtr space;
  1807. int x, y;
  1808. long flags;
  1809. /*Does a 3-D finger in a space*/
  1810. {
  1811.     int left, right, bottom, top;
  1812.     ObjPtr contents;
  1813.     ThingListPtr runner;
  1814.     FuncTyp method;
  1815.  
  1816.     Get2DIntBounds(space, &left, &right, &bottom, &top);
  1817.  
  1818.     StartSpace(space, left, right, bottom, top, PICKSPACE, 0);
  1819.  
  1820.     /*Pick the contents of the space*/
  1821.     contents = GetVar(space, CONTENTS);
  1822.  
  1823.     runner = LISTOF(contents);
  1824.     while (runner)
  1825.     {
  1826.     if (IsObject(runner -> thing))
  1827.     {
  1828.         PickObject(runner -> thing, PR_CENTER);
  1829.         method = GetMethod(runner -> thing, PICKPOINT);
  1830.         if (method)
  1831.         {
  1832.         (*method)(runner -> thing);
  1833.         }
  1834.     }
  1835.     runner = runner -> next;
  1836.     }
  1837.  
  1838.     StopSpace(PICKSPACE);
  1839.  
  1840.     if (0 == (flags & F_EXTEND))
  1841.     {
  1842.     DeselectAll();
  1843.     }
  1844.  
  1845.     if (spacePickedObject)
  1846.     {
  1847.     if ((bestPickVertex >= 0) && (method = GetMethod(spacePickedObject, 
  1848.         SELECTSELPOINT)))
  1849.     {
  1850.         if (!IsSelected(spacePickedObject))
  1851.         {
  1852.         Select(spacePickedObject, true);
  1853.         }
  1854.         (*method)(spacePickedObject, bestPickVertex, flags);
  1855.     }
  1856.     else
  1857.     {
  1858.         /*Select or deselect as a whole*/
  1859.         if ((flags & F_EXTEND) && IsSelected(spacePickedObject))
  1860.         {
  1861.         Select(spacePickedObject, false);
  1862.         }
  1863.         else
  1864.         {
  1865.         Select(spacePickedObject, true);
  1866.         }
  1867.     }
  1868.     return ObjTrue;
  1869.     }
  1870.     return ObjFalse;
  1871. }
  1872.  
  1873. static ObjPtr TurnDialSpace(space, whichDial, delta, flags)
  1874. ObjPtr space;
  1875. int whichDial;
  1876. real delta;
  1877. long flags;
  1878. /*Turns dial whichDial in space space by delta*/
  1879. {
  1880.     ObjPtr var;
  1881.     ObjPtr observer;
  1882.     real axis[3];
  1883.     real phi;
  1884.     real mv;
  1885.     real eyePosn[3];
  1886.     real forward[3], up[3], side[3];
  1887.     real clips[2];
  1888.  
  1889.     phi = delta * DIALROTFACTOR * M_PI_2;
  1890.     mv = delta * DIALMOVEFACTOR * M_PI_2;
  1891.  
  1892.     observer = GetObjectVar("TurnDialSpace", space, OBSERVER);
  1893.     if (observer)
  1894.     {
  1895.     GetObserverLocation(eyePosn, observer);
  1896.     GetAdjustedVectors(forward, up, observer);
  1897.     CROSS(up, forward, side);
  1898.  
  1899.     switch (whichDial)
  1900.     {
  1901.         case 7:        /*Motion in x direction*/
  1902.         if (flags & F_OPTIONDOWN)
  1903.         {
  1904.             eyePosn[0] -= mv;
  1905.         }
  1906.         else
  1907.         {
  1908.             eyePosn[0] += side[0] * mv;
  1909.             eyePosn[1] += side[1] * mv;
  1910.             eyePosn[2] += side[2] * mv;
  1911.         }
  1912.         var = NewRealArray(1, 3L);
  1913.         CArray2Array(var, eyePosn);
  1914.         SetVar(observer, LOCATION, var);
  1915.         ImInvalid(observer);
  1916.         break;
  1917.         case 6:        /*Rotation about y*/
  1918.         if (flags & F_OPTIONDOWN)
  1919.         {
  1920.             axis[0] = 0.0;
  1921.             axis[1] = 1.0;
  1922.             axis[2] = 0.0;
  1923.             RotateObserver(observer, axis, phi, false);
  1924.         }
  1925.         else
  1926.         {
  1927.             RotateObserver(observer, up, phi, false);
  1928.         }
  1929.         break;
  1930.         case 5:        /*Motion in y direction*/
  1931.         if (flags & F_OPTIONDOWN)
  1932.         {
  1933.             eyePosn[1] -= mv;
  1934.         }
  1935.         else
  1936.         {
  1937.             eyePosn[0] -= up[0] * mv;
  1938.             eyePosn[1] -= up[1] * mv;
  1939.             eyePosn[2] -= up[2] * mv;
  1940.         }
  1941.         var = NewRealArray(1, 3L);
  1942.         CArray2Array(var, eyePosn);
  1943.         SetVar(observer, LOCATION, var);
  1944.         ImInvalid(observer);
  1945.         break;
  1946.         case 4:        /*Rotation to top/bottom*/
  1947.         if (flags & F_OPTIONDOWN)
  1948.         {
  1949.             axis[0] = -1.0;
  1950.             axis[1] = 0.0;
  1951.             axis[2] = 0.0;
  1952.             RotateObserver(observer, axis, phi, false);
  1953.         }
  1954.         else
  1955.         {
  1956.             RotateObserver(observer, side, phi, false);
  1957.         }
  1958.         break;
  1959.         case 3:        /*Motion in z direction, or forward*/
  1960.         if (flags & F_OPTIONDOWN)
  1961.         {
  1962.             eyePosn[2] -= mv;
  1963.         }
  1964.         else
  1965.         {
  1966.             eyePosn[0] += forward[0] * mv;
  1967.             eyePosn[1] += forward[1] * mv;
  1968.             eyePosn[2] += forward[2] * mv;
  1969.  
  1970.             var = GetVar(observer, FOCUSDIST);
  1971.             if (var)
  1972.             {
  1973.             SetVar(observer, FOCUSDIST, NewReal(GetReal(var) - mv));
  1974.             }
  1975.             var = GetVar(observer, VIEWCLIP);
  1976.             if (var)
  1977.             {
  1978.             Array2CArray(clips, var);
  1979.             clips[0] -= mv;
  1980.             clips[1] -= mv;
  1981.             var = NewRealArray(1, 2L);
  1982.             CArray2Array(var, clips);
  1983.             SetVar(observer, VIEWCLIP, var);
  1984.             }
  1985.         }
  1986.         var = NewRealArray(1, 3L);
  1987.         CArray2Array(var, eyePosn);
  1988.         SetVar(observer, LOCATION, var);
  1989.         ImInvalid(observer);
  1990.         break;
  1991.         case 2:        /*Rotation about z*/
  1992.         if (flags & F_OPTIONDOWN)
  1993.         {
  1994.             axis[0] = 0.0;
  1995.             axis[1] = 0.0;
  1996.             axis[2] = 1.0;
  1997.             RotateObserver(observer, axis, -phi, false);
  1998.         }
  1999.         else
  2000.         {
  2001.             RotateObserver(observer, forward, phi, false);
  2002.         }
  2003.         break;
  2004.         case 0:        /*Change clipping plane*/
  2005.         var = GetVar(observer, VIEWCLIP);
  2006.         if (var)
  2007.         {
  2008.             Array2CArray(clips, var);
  2009.             if (flags & F_OPTIONDOWN)
  2010.             {
  2011.             clips[1] += mv;
  2012.             if (clips[1] < clips[0] + 0.001) clips[1] = clips[0] + 0.001;
  2013.             }
  2014.             else
  2015.             {
  2016.             clips[0] += mv;
  2017.             if (clips[1] < clips[0] + 0.001) clips[0] = clips[1] - 0.001;
  2018.             }
  2019.             var = NewRealArray(1, 2L);
  2020.             CArray2Array(var, clips);
  2021.             SetVar(observer, VIEWCLIP, var);
  2022.             ImInvalid(observer);
  2023.         }
  2024.         break;
  2025.         case 1:        /*Change angle or focus width*/
  2026.         if (flags & F_OPTIONDOWN)
  2027.         {
  2028.             /*Change binocular spacing*/
  2029.             var = GetVar(observer, BINOCULARITY);
  2030.             if (var)
  2031.             {
  2032.             real val;
  2033.             val = GetReal(var) + delta * DIALBINOCFACTOR;
  2034.             if (val < 0.0) val = 0.0;
  2035.             SetVar(observer, BINOCULARITY, NewReal(val));
  2036.             ImInvalid(observer);
  2037.             }
  2038.         }
  2039.         else
  2040.         {
  2041.             /*Change angle or field width*/
  2042.             Bool isOrtho = false;
  2043.  
  2044.             MakeVar(observer, VIEWTYPE);
  2045.             var = GetVar(observer, VIEWTYPE);
  2046.             if (var)
  2047.             {
  2048.             if (GetInt(var) == VT_ORTHOGRAPHIC)
  2049.             {
  2050.                 isOrtho = true;
  2051.             }
  2052.             }
  2053.  
  2054.             var = GetVar(observer, VIEWFIELD);
  2055.             if (var)
  2056.             {
  2057.             if (isOrtho)
  2058.             {
  2059.                 real val;
  2060.                 val = GetReal(var) + delta * DIALWIDTHFACTOR;
  2061.                 if (val < 0.001) val = 0.001;
  2062.                 SetVar(observer, VIEWFIELD, NewReal(val));
  2063.             }
  2064.             else
  2065.             {
  2066.                 real val;
  2067.                 val = GetReal(var) + delta * DIALANGLEFACTOR * 180.0;
  2068.                 if (val < 0.1) val = 0.1;
  2069.                 if (val > 179.0) val = 179.0;
  2070.                 SetVar(observer, VIEWFIELD, NewReal(val));
  2071.             }
  2072.             ImInvalid(observer);
  2073.             }
  2074.         }
  2075.         break;
  2076.     }
  2077.     }
  2078.     return ObjTrue;
  2079. }
  2080.  
  2081. static ObjPtr PressSpace(object, x, y, flags)
  2082. ObjPtr object;
  2083. int x, y;
  2084. long flags;
  2085. /*Does a press in a space beginning at x and y.  Returns
  2086.   true iff the press really was in the space.*/
  2087. {
  2088. #ifdef INTERACTIVE
  2089.     int left, right, bottom, top;
  2090.     ObjPtr tool;
  2091.     int spaceTool;
  2092.  
  2093.     /*Get the space tool*/
  2094.     tool = GetIntVar("PressSpace", object, EDITTOOL);
  2095.     if (!tool)
  2096.     {
  2097.     return ObjFalse;
  2098.     }
  2099.     spaceTool = GetInt(tool);
  2100.  
  2101.     Get2DIntBounds(object, &left, &right, &bottom, &top);
  2102.  
  2103.     if (x >= left && x <= right && y >= bottom && y <= top)
  2104.     {
  2105.     if (TOOL(flags) == T_HELP)
  2106.     {
  2107.         ContextHelp(object);
  2108.         return ObjTrue;
  2109.     }
  2110.  
  2111.     /*It's a click in the space*/
  2112.     switch (spaceTool)
  2113.     {
  2114.         case ST_FLYING:
  2115.         {
  2116.             /*Toggle flying*/
  2117.             Bool flying;
  2118.             ObjPtr observer;
  2119.             MakeMeCurrent(object);
  2120.  
  2121.             observer = GetObjectVar("PressSpace", object, OBSERVER);
  2122.             if (!observer) return ObjFalse;
  2123.         
  2124.             flying = GetPredicate(observer, FLYING);
  2125.             flying = flying ? false : true;
  2126.             if (flying)
  2127.             {
  2128.             WakeMe(observer, MARKTIME, Clock() + 0.001);
  2129.             }
  2130.             else
  2131.             {
  2132.             DoNotDisturb(observer, MARKTIME);
  2133.             }
  2134.         
  2135.             SetVar(observer, FLYING, flying ? ObjTrue : ObjFalse);
  2136.         }
  2137.         return ObjTrue;
  2138.         break;
  2139.         case ST_FINGER:
  2140.         MakeMeCurrent(object);
  2141.         if (TOOL(flags) == T_ROTATE)
  2142.         {
  2143.             return RotateSpace(object, x, y, flags);
  2144.         }
  2145.         else
  2146.         {
  2147.             ObjPtr test;
  2148.             test = FingerSpace(object, x, y, flags);
  2149.             if (IsTrue(test))
  2150.             {
  2151.                 MoveSpace(object, x, y, flags);
  2152.             return ObjTrue;
  2153.             }
  2154.         }
  2155.     }
  2156.     return ObjFalse;
  2157.     }
  2158.     else
  2159. #endif
  2160.     {
  2161.     return ObjFalse;
  2162.     }
  2163. }
  2164.  
  2165. int dsn = 0;
  2166.  
  2167. #ifdef PROTO
  2168. void DrawSpaceContents(ObjPtr space, int left, int right, int bottom, int top, int viewType)
  2169. #else
  2170. void DrawSpaceContents(space, left, right, bottom, top, viewType)
  2171. ObjPtr space;
  2172. int left, right, bottom, top;
  2173. int viewType;
  2174. #endif
  2175. {
  2176. #ifdef GRAPHICS
  2177.     ObjPtr contents;
  2178.     Bool anyTransparent;        /*True iff any objects are transparent*/
  2179.     ThingListPtr drawList;        /*List of objects to draw*/
  2180.     ObjPtr frontPanel;
  2181.     int prefDrawMoving;            /*Preferred draw moving style*/
  2182.     ObjPtr var;
  2183.     Bool isCached;
  2184.     ObjPtr cache;
  2185.  
  2186.     frontPanel = GetVar(space, FRONTPANEL);
  2187.     if (frontPanel &&
  2188.     (GetVar(frontPanel, BACKGROUND) || GetPredicate(frontPanel, TEMPOBSCURED)))
  2189.     {
  2190.     /*Front panel has a background.  Don't need to draw space*/
  2191.     return;
  2192.     }
  2193.  
  2194.     /*Set a new viewport to the current area*/
  2195.     StartSpace(space, left, right, bottom, top, DRAWSPACE, viewType);
  2196.  
  2197.     /*Figure out the range of drawing qualities to use.*/
  2198.     if (interactiveMoving)
  2199.     {
  2200.     /*It's interactive.  Figure out what quality to use*/
  2201.     prefDrawMoving = GetPrefInteger(PREF_DRAWMOVING);
  2202.  
  2203.     if (prefDrawMoving == DM_SKELETON)
  2204.     {
  2205.         minDrawingQuality = DQ_MIN;
  2206.         maxDrawingQuality = DQ_OUTLINE;
  2207.     }
  2208.     else if (prefDrawMoving == DM_FULL)
  2209.     {
  2210.         minDrawingQuality = DQ_MIN;
  2211.         maxDrawingQuality = DQ_MAX;
  2212.     }
  2213.     else if (prefDrawMoving == DM_AUTO)
  2214.     {
  2215.         var = GetVar(space, DRAWINGTIME);
  2216.         if (var)
  2217.         {
  2218.         long drawingTime;
  2219.         /*There is a drawing time recorded.  Get it*/
  2220.         drawingTime = GetInt(var);
  2221.  
  2222.         if (drawingTime < MAXMOVETIME)
  2223.         {
  2224.             /*It's fast enough, might as well do it fully*/
  2225.             minDrawingQuality = DQ_MIN;
  2226.             maxDrawingQuality = DQ_MAX;
  2227.         }
  2228.         else
  2229.         {
  2230.             /*Do outline*/
  2231.             minDrawingQuality = DQ_MIN;
  2232.             maxDrawingQuality = DQ_OUTLINE;
  2233.         }
  2234.         }
  2235.         else
  2236.         {
  2237.         /*Assume we have to do full drawing*/
  2238.         minDrawingQuality = DQ_MIN;
  2239.         maxDrawingQuality = DQ_MAX;
  2240.         }
  2241.     }
  2242.     }
  2243.     else
  2244.     {
  2245.     /*Not interactive.  Just use full quality*/
  2246.     minDrawingQuality = DQ_MIN;
  2247.     maxDrawingQuality = DQ_MAX;
  2248.     }
  2249.  
  2250.     /*Draw the contents of the space*/
  2251.     contents = GetVar(space, CONTENTS);
  2252.  
  2253.     /*First draw opaque objects*/
  2254.     anyTransparent = false;
  2255.     drawList = LISTOF(contents);
  2256.     drawingTransparent = false;
  2257.     while (drawList)
  2258.     {
  2259.     if (IsObject(drawList -> thing))
  2260.     {
  2261.         if (GetPredicate(drawList -> thing, ISTRANSPARENT))
  2262.         {
  2263.         anyTransparent = true;
  2264.         }
  2265.         qualityDrawnSoFar = DQ_MIN;
  2266.  
  2267.         if (maxDrawingQuality >= DQ_FULL && 
  2268.         GetPredicate(drawList -> thing, CACHEGRAPHICS))
  2269.         {
  2270.         cacheMade = false;
  2271.         MakeVar(drawList -> thing, CACHEDOPAQUE);
  2272.         if (cacheMade)
  2273.         {
  2274.             cache = GetVar(drawList -> thing, CACHEDOPAQUE);
  2275.             if (cache)
  2276.             {
  2277.             OpenGraphObj(cache);
  2278.             }
  2279.             else
  2280.             {
  2281.             cache = NewOpenGraphObj();
  2282.             SetVar(drawList -> thing, CACHEDOPAQUE, cache);
  2283.             }
  2284.             DrawVisObject(drawList -> thing);
  2285.             CloseGraphObj(cache);
  2286.             DrawGraphObj(cache);
  2287.         }
  2288.         else
  2289.         {
  2290.             /*Just draw it*/
  2291.             cache = GetVar(drawList -> thing, CACHEDOPAQUE);
  2292.             if (cache)
  2293.             {
  2294.             DrawGraphObj(cache);
  2295.             }
  2296.             else
  2297.             {
  2298.             DrawVisObject(drawList -> thing);
  2299.             }
  2300.         }
  2301.         }
  2302.         else
  2303.         {
  2304.         DrawVisObject(drawList -> thing);
  2305.         }
  2306.         if (maxDrawingQuality < DQ_FULL)
  2307.         {
  2308.         NeedToDrawFullQuality(drawList -> thing);
  2309.         }
  2310.     }
  2311.     drawList = drawList -> next;
  2312.     }
  2313.  
  2314.     /*Now draw transparent objects*/
  2315.     if (anyTransparent && rgbp && maxDrawingQuality >= DQ_FULL)
  2316.     {
  2317.     BeginMask(true, true, true, false);
  2318.     blendfunction(BF_SA, BF_MSC);
  2319.  
  2320.     drawingTransparent = true;
  2321.     drawList = LISTOF(contents);
  2322.     while (drawList)
  2323.     {
  2324.         minDrawingQuality = DQ_FULL;    
  2325.         maxDrawingQuality = DQ_FULL;
  2326.         if (IsObject(drawList -> thing))
  2327.         {
  2328.         qualityDrawnSoFar = DQ_MIN;
  2329.         if (maxDrawingQuality >= DQ_FULL && 
  2330.             GetPredicate(drawList -> thing, CACHEGRAPHICS))
  2331.         {
  2332.             cacheMade = false;
  2333.             MakeVar(drawList -> thing, CACHEDTRANSPARENT);
  2334.             if (cacheMade)
  2335.             {
  2336.             cache = GetVar(drawList -> thing, CACHEDTRANSPARENT);
  2337.             if (cache)
  2338.             {
  2339.                 OpenGraphObj(cache);
  2340.             }
  2341.             else
  2342.             {
  2343.                 cache = NewOpenGraphObj();
  2344.                 SetVar(drawList -> thing, CACHEDTRANSPARENT, cache);
  2345.             }
  2346.             DrawVisObject(drawList -> thing);
  2347.             CloseGraphObj(cache);
  2348.             DrawGraphObj(cache);
  2349.             }
  2350.             else
  2351.             {
  2352.             /*Just draw it*/
  2353.             cache = GetVar(drawList -> thing, CACHEDTRANSPARENT);
  2354.             if (cache)
  2355.             {
  2356.                 DrawGraphObj(cache);
  2357.             }
  2358.             else
  2359.             {
  2360.                 DrawVisObject(drawList -> thing);
  2361.             }
  2362.             }
  2363.         }
  2364.         else
  2365.         {
  2366.             DrawVisObject(drawList -> thing);
  2367.         }
  2368.         if (maxDrawingQuality < DQ_FULL)
  2369.         {
  2370.             NeedToDrawFullQuality(drawList -> thing);
  2371.         }
  2372.         }
  2373.         drawList = drawList -> next;
  2374.     }
  2375.     blendfunction(BF_ONE, BF_ZERO);
  2376.     EndMask();
  2377.     drawingTransparent = false;
  2378.     }
  2379.  
  2380.     if (maxDrawingQuality >= DQ_FULL)
  2381.     {
  2382.     /*End the timing of the full drawing*/
  2383.     SetVar(space, DRAWINGTIME, NewInt(GetPictureTime()));
  2384.     }
  2385.  
  2386.     StopSpace(DRAWSPACE);
  2387. #endif
  2388. }
  2389.  
  2390. #ifdef PROTO
  2391. Bool RotateObserver(ObjPtr observer, real axis[3], real phi, Bool constrain)
  2392. #else
  2393. Bool RotateObserver(observer, axis, phi, constrain)
  2394. ObjPtr observer;
  2395. real axis[3];
  2396. real phi;
  2397. Bool constrain;
  2398. #endif
  2399. /*Rotates an observer by phi around axis.  If constrain, constrains axis as
  2400.   a side effect.*/
  2401. {
  2402.     ObjPtr var;
  2403.     real focusPoint[3];
  2404.     real location[3];
  2405.     real forwardVector[3];
  2406.     real focusDist;
  2407.     Quaternion rotQuat;
  2408.     Quaternion deltaRotQuat;
  2409.     Quaternion newRot;
  2410.  
  2411.     /*Now that there's a new delta, rotate and redraw*/
  2412.     if (constrain)
  2413.     {
  2414.     int k, best;
  2415.     float curDot, maxDot = 0.0;
  2416.     float rr;
  2417.  
  2418.     /*Constrain the axis to the nearest ortho axis*/
  2419.     for (k = 0; k < 3; ++k)
  2420.     {
  2421.         curDot =
  2422.         axis[0] * Identity[k][0] +
  2423.         axis[1] * Identity[k][1] +
  2424.         axis[2] * Identity[k][2];
  2425.  
  2426.         if (ABS(curDot) > ABS(maxDot))
  2427.         {
  2428.         /*It's a better choice*/
  2429.         maxDot = curDot;
  2430.         best = k;
  2431.         }
  2432.     }
  2433.  
  2434.     /*Now we have a best match*/
  2435.     axis[0] = Identity[best][0];
  2436.     axis[1] = Identity[best][1];
  2437.     axis[2] = Identity[best][2];
  2438.     if (maxDot < 0.0)
  2439.     {
  2440.         axis[0] = -axis[0];
  2441.         axis[1] = -axis[1];
  2442.         axis[2] = -axis[2];
  2443.     }
  2444.  
  2445.     /*Normalize the axis*/
  2446.     rr = 1.0 / sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
  2447.     axis[0] *= rr;
  2448.     axis[1] *= rr;
  2449.     axis[2] *= rr;
  2450.     }
  2451.  
  2452.     /*Get current focus point*/
  2453.     GetFocusPoint(focusPoint, observer);
  2454.  
  2455.     /*Now make the change rotation quaternion*/
  2456.     RotToQuaternion(axis, phi, deltaRotQuat);
  2457.  
  2458.     /*Apply it to the rotation quaternion*/
  2459.     MakeVar(observer, ROTQUAT);
  2460.     var = GetVar(observer, ROTQUAT);
  2461.     if (!var)
  2462.     {
  2463.     ReportError("RotateObserver", "No rotation quaternion");
  2464.     return;
  2465.     }
  2466.     Array2CArray(rotQuat, var);
  2467.  
  2468.     QMult(rotQuat, deltaRotQuat, newRot);
  2469.  
  2470.     var = NewRealArray(1, 4L);
  2471.     CArray2Array(var, newRot);
  2472.     SetVar(observer, ROTQUAT, var);
  2473.  
  2474.     /*Get new forward vector*/
  2475.     GetForwardVector(forwardVector, observer);
  2476.  
  2477.     var = GetVar(observer, FOCUSDIST);
  2478.     if (var)
  2479.     {
  2480.     focusDist = GetReal(var);
  2481.     }
  2482.     else
  2483.     {
  2484.     focusDist = INITEYEDIST;
  2485.     }
  2486.  
  2487.     /*Make a new location*/
  2488.     location[0] = focusPoint[0] - forwardVector[0] * focusDist;
  2489.     location[1] = focusPoint[1] - forwardVector[1] * focusDist;
  2490.     location[2] = focusPoint[2] - forwardVector[2] * focusDist;
  2491.  
  2492.     /*Put back the location vector*/
  2493.     var = NewRealArray(1, 3L);
  2494.     CArray2Array(var, location);
  2495.     SetVar(observer, LOCATION, var);
  2496.  
  2497.     ImInvalid(observer);
  2498.     return true;
  2499. }
  2500.  
  2501. void PrintMatrix(real m[4][4])
  2502. /*Prints a matrix*/
  2503. {
  2504.     printf("%10g %10g %10g %10g\n", m[0][0], m[0][1], m[0][2], m[0][3]);
  2505.     printf("%10g %10g %10g %10g\n", m[1][0], m[1][1], m[1][2], m[1][3]);
  2506.     printf("%10g %10g %10g %10g\n", m[2][0], m[2][1], m[2][2], m[2][3]);
  2507.     printf("%10g %10g %10g %10g\n", m[3][0], m[3][1], m[3][2], m[3][3]);
  2508. }
  2509.  
  2510. #ifdef PROTO
  2511. void RotToQuaternion(real axis[3], real amount, Quaternion r)
  2512. #else
  2513. void RotToQuaternion(axis, amount, r)
  2514. real axis[3];
  2515. real amount;
  2516. Quaternion r;
  2517. #endif
  2518. /*Converts a rotation by amount around axis to a quaternion*/
  2519. {
  2520.     real absVal, multiplier1, multiplier2;
  2521.  
  2522.     absVal = sqrt(SQUARE(axis[0]) + SQUARE(axis[1]) + SQUARE(axis[2]));
  2523.     if (absVal > 0.0)
  2524.     {
  2525.     multiplier1 = rsin(amount * 0.5) / absVal;
  2526.     multiplier2 = rcos(amount * 0.5);
  2527.  
  2528.     r[0] = multiplier2;
  2529.     r[1] = axis[0] * multiplier1;
  2530.     r[2] = axis[1] * multiplier1;
  2531.     r[3] = axis[2] * multiplier1;
  2532.     }
  2533.     else
  2534.     {
  2535.     r[0] = 1.0;
  2536.     r[1] = 0.0;
  2537.     r[2] = 0.0;
  2538.     r[3] = 0.0;
  2539.     }
  2540. }
  2541.  
  2542. #ifdef PROTO
  2543. void QuaternionToRot(Quaternion r, real axis[3], real *amount)
  2544. #else
  2545. void QuaternionToRot(r, axis, amount)
  2546. real axis[3];
  2547. real *amount;
  2548. Quaternion r;
  2549. #endif
  2550. /*Converts a quaternion to a rotation by amount around axis*/
  2551. {
  2552.     real absVal, multiplier1, multiplier2;
  2553.  
  2554.     absVal = sqrt(SQUARE(r[1]) + SQUARE(r[2]) + SQUARE(r[3]));
  2555.     if (absVal > 0.0)
  2556.     {
  2557.     *amount = racos(r[0]) * 2;
  2558.     axis[0] = r[1] / absVal;
  2559.     axis[1] = r[2] / absVal;
  2560.     axis[2] = r[3] / absVal;
  2561.     }
  2562.     else
  2563.     {
  2564.     *amount = 0;
  2565.     axis[0] = 0.0;
  2566.     axis[1] = 0.0;
  2567.     axis[2] = 1.0;
  2568.     }
  2569. }
  2570.  
  2571. #ifdef PROTO
  2572. void MatrixToQuaternion(real m[3][3], Quaternion q)
  2573. #else
  2574. void MatrixToQuaternion(m, q)
  2575. real m[3][3];
  2576. Quaternion q;
  2577. #endif
  2578. /*Converts a matrix (rotation only) to a quaternion*/
  2579. {
  2580.     real greatestSquare;
  2581.     char whichGreatest;
  2582.     real square;
  2583.     real dividend;
  2584.  
  2585. #undef w
  2586. #undef x
  2587. #undef y
  2588. #undef z
  2589. #define w q[0]
  2590. #define x q[1]
  2591. #define y q[2]
  2592. #define z q[3]
  2593.  
  2594.     /*Solving along diagonal, see which is greatest*/
  2595.     greatestSquare = 0.0;
  2596.     whichGreatest = ' ';
  2597.  
  2598.     /*Test solving for x*/
  2599.     if ((square = 1.0 - m[2][2] + m[0][0] - m[1][1]) > greatestSquare)
  2600.     {
  2601.     greatestSquare = square;
  2602.     whichGreatest = 'x';
  2603.     }
  2604.  
  2605.     /*Test solving for y*/
  2606.     if ((square = 1.0 - m[0][0] + m[1][1] - m[2][2]) > greatestSquare)
  2607.     {
  2608.     greatestSquare = square;
  2609.     whichGreatest = 'y';
  2610.     }
  2611.  
  2612.     /*Test solving for z*/
  2613.     if ((square = 1.0 - m[1][1] + m[2][2] - m[0][0]) > greatestSquare)
  2614.     {
  2615.     greatestSquare = square;
  2616.     whichGreatest = 'z';
  2617.     }
  2618.  
  2619.     if (whichGreatest == 'x')
  2620.     {
  2621.     /*Arbitrarily choose +x*/
  2622.     x = sqrt(greatestSquare) * 0.5;
  2623.     dividend = 1.0 / (4.0 * x);
  2624.  
  2625.     /*Calculate y*/
  2626.     y = (m[0][1] + m[1][0]) * dividend;
  2627.  
  2628.     /*Calculate z*/
  2629.     z = (m[0][2] + m[2][0]) * dividend;
  2630.  
  2631.     /*Calculate w*/
  2632.     w = 2.0 * (2.0 * y * z - m[2][1]) * dividend; 
  2633.     }
  2634.     else if (whichGreatest == 'y')
  2635.     {
  2636.     /*Arbitrarily choose +y*/
  2637.     y = sqrt(greatestSquare) * 0.5;
  2638.     dividend = 1.0 / (4.0 * y);
  2639.  
  2640.     /*Calculate x*/
  2641.     x = (m[0][1] + m[1][0]) * dividend;
  2642.  
  2643.     /*Calculate z*/
  2644.     z = (m[2][1] + m[1][2]) * dividend;
  2645.  
  2646.     /*Calculate w*/
  2647.     w = 2.0 * (2.0 * x * z - m[0][2]) * dividend; 
  2648.     }
  2649.     else if (whichGreatest == 'z')
  2650.     {
  2651.     /*Arbitrarily choose +z*/
  2652.     z = sqrt(greatestSquare) * 0.5;
  2653.     dividend = 1.0 / (4.0 * z);
  2654.  
  2655.     /*Calculate x*/
  2656.     x = (m[0][2] + m[2][0]) * dividend;
  2657.  
  2658.     /*Calculate y*/
  2659.     y = (m[2][1] + m[1][2]) * dividend;
  2660.  
  2661.     /*Calculate w*/
  2662.     w = 2.0 * (2.0 * x * y - m[1][0]) * dividend; 
  2663.     }
  2664.     else
  2665.     {
  2666.     /*It has to be [1, [0 0 0]]*/
  2667.     q[0] = 1.0;
  2668.     q[1] = 0.0;
  2669.     q[2] = 0.0;
  2670.     q[3] = 0.0;
  2671.     }
  2672. #undef w
  2673. #undef x
  2674. #undef y
  2675. #undef z
  2676. }
  2677.  
  2678. void QInvert(Quaternion s, Quaternion r)
  2679. /*Inverts quaternion s into result r, assuming |s| == 1*/
  2680. {
  2681.     r[0] = s[0];
  2682.     r[1] = -s[1];
  2683.     r[2] = -s[2];
  2684.     r[3] = -s[3];
  2685. }
  2686.  
  2687. void QMult(Quaternion q1, Quaternion q2, Quaternion r)
  2688. /*Multiplies two quaternions giving a third*/
  2689. {
  2690.     real qp[3];
  2691.  
  2692.     r[0] = q1[0] * q2[0] - (q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3]);
  2693.  
  2694.     CROSS(&(q1[1]), &(q2[1]), qp);
  2695.     r[1] = q1[0] * q2[1] + q2[0] * q1[1] + qp[0];
  2696.     r[2] = q1[0] * q2[2] + q2[0] * q1[2] + qp[1];
  2697.     r[3] = q1[0] * q2[3] + q2[0] * q1[3] + qp[2];
  2698. }
  2699.  
  2700. void QDouble(Quaternion q1, Quaternion q2, Quaternion r)
  2701. /*Does the Double operation, but not like [Shoemake, 1985]*/
  2702. {
  2703.     Quaternion qtemp1, qtemp2;
  2704.  
  2705. #if 0
  2706.     /*This is from [Shoemake, 1985].  I don't understand the logic, and
  2707.     also, it doesn't work.*/
  2708.  
  2709.     QMult(q1, q2, qtemp1);
  2710.     QMult(qtemp1, q2, qtemp2);
  2711.  
  2712.     r[0] = qtemp2[0] * 2.0 - q1[0];
  2713.     r[1] = qtemp2[1] * 2.0 - q1[1];
  2714.     r[2] = qtemp2[2] * 2.0 - q1[2];
  2715.     r[3] = qtemp2[3] * 2.0 - q1[3];
  2716. #else
  2717.     /*This, however, works fine*/
  2718.     QInvert(q1, qtemp1);
  2719.     QMult(q2, qtemp1, qtemp2);
  2720.     QMult(qtemp2, q2, r);
  2721. #endif
  2722. }
  2723.  
  2724. void Slerp(Quaternion q1, Quaternion q2, real u, Quaternion r)
  2725. /*Does the slerp operation in [Shoemake 1985]*/
  2726. {
  2727.     real theta, costheta, w1, w2, sintheta;
  2728.  
  2729.     costheta = q1[0] * q2[0] + q1[1] * q2[1] + q1[2] * q2[2] + q1[3] * q2[3];
  2730.     theta = racos(costheta);
  2731.     sintheta = rsin(theta);
  2732.     if (sintheta > 0.0)
  2733.     {
  2734.     w1 = rsin((1.0 - u) * theta) / sintheta;
  2735.     w2 = rsin(u * theta) / sintheta;
  2736.     }
  2737.     else
  2738.     {
  2739.     /*They're the same quaternion, so who cares?*/
  2740.     w1 = 1.0;
  2741.     w2 = 0.0;
  2742.     }
  2743.  
  2744.     r[0] = w1 * q1[0] + w2 * q2[0];
  2745.     r[1] = w1 * q1[1] + w2 * q2[1];
  2746.     r[2] = w1 * q1[2] + w2 * q2[2];
  2747.     r[3] = w1 * q1[3] + w2 * q2[3];
  2748. }
  2749.  
  2750. #ifdef PROTO
  2751. void QBisect(Quaternion q1, Quaternion q2, Quaternion r)
  2752. #else
  2753. void QBisect(q1, q2, r)
  2754. Quaternion q1, q2, r;
  2755. #endif
  2756. /*Does the Bisect operation in [Shoemake, 1985]*/
  2757. {
  2758.     real absVal;
  2759.  
  2760. #if 1
  2761.     r[0] = q1[0] + q2[0];
  2762.     r[1] = q1[1] + q2[1];
  2763.     r[2] = q1[2] + q2[2];
  2764.     r[3] = q1[3] + q2[3];
  2765.  
  2766.     absVal = sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3]);
  2767.     r[0] /= absVal;
  2768.     r[1] /= absVal;
  2769.     r[2] /= absVal;
  2770.     r[3] /= absVal;
  2771. #else
  2772.     Slerp(q1, q2, 0.5, r);
  2773. #endif
  2774. }
  2775.  
  2776. #ifdef PROTO
  2777. void QRot(real v[3], Quaternion q, real r[3])
  2778. #else
  2779. void QRot(v, q, r)
  2780. real v[3];
  2781. Quaternion q;
  2782. real r[3];
  2783. #endif
  2784. /*Rotates v by q into r*/
  2785. {
  2786.     Quaternion qInv, qTemp;
  2787.     Quaternion qVec, qRes;
  2788.  
  2789.     QInvert(q, qInv);
  2790.     qVec[0] = 0.0;
  2791.     qVec[1] = v[0];
  2792.     qVec[2] = v[1];
  2793.     qVec[3] = v[2];
  2794.  
  2795.     QMult(qInv, qVec, qTemp);
  2796.     QMult(qTemp, q, qRes);
  2797.  
  2798.     r[0] = qRes[1];
  2799.     r[1] = qRes[2];
  2800.     r[2] = qRes[3];
  2801. }
  2802.  
  2803. void QDelta(Quaternion q1, Quaternion q2, Quaternion r)
  2804. /*Put in r the quaternion such that q1 r = q2*/
  2805. {
  2806.     Quaternion q1inv;
  2807.  
  2808.     QInvert(q1, q1inv);
  2809.     QMult(q1inv, q2, r);
  2810. }
  2811.  
  2812. TestObserver()
  2813. /*Does an observer test in the current window*/
  2814. {
  2815.     ObjPtr observer;
  2816.     real *forward, *up;
  2817.  
  2818.     if (selWinInfo)
  2819.     {
  2820.     observer = FindObserver(selWinInfo);
  2821.     if (observer)
  2822.     {
  2823.         ObjPtr var;
  2824.         real *elements;
  2825.         real side[3], m[4][4];
  2826.         Quaternion q, qInv, qTest, qVec;
  2827.  
  2828.         var = GetVar(observer, LOCATION);
  2829.         if (var)
  2830.         {
  2831.         forward = (real *) ELEMENTS(var);
  2832.         printf("Location = [%g %g %g]\n",
  2833.             forward[0], forward[1], forward[2]);
  2834.         }
  2835.  
  2836.         var = GetVar(observer, FORWARDVECTOR);
  2837.         if (var)
  2838.         {
  2839.         forward = (real *) ELEMENTS(var);
  2840.         printf("Forward = [%g %g %g]\n",
  2841.             forward[0], forward[1], forward[2]);
  2842.         }
  2843.  
  2844.         var = GetVar(observer, UPVECTOR);
  2845.         if (var)
  2846.         {
  2847.         up = (real *) ELEMENTS(var);
  2848.         printf("Up = [%g %g %g]\n",
  2849.             up[0], up[1], up[2]);
  2850.         }
  2851.         /* generate the vector side from the
  2852.          * 2 vectors view forward and world up 
  2853.          */
  2854.         CROSS(forward, up, side);
  2855.         NORMALIZE(side);
  2856.  
  2857.         m[0][0] = side[0];
  2858.         m[1][0] = side[1];
  2859.         m[2][0] = side[2];
  2860.         m[3][0] = 0.0;
  2861.  
  2862.         m[0][1] = up[0];
  2863.         m[1][1] = up[1];
  2864.         m[2][1] = up[2];
  2865.         m[3][1] = 0.0;
  2866.  
  2867.         m[0][2] = -forward[0];
  2868.         m[1][2] = -forward[1];
  2869.         m[2][2] = -forward[2];
  2870.         m[3][2] = 0.0;
  2871.  
  2872.         m[0][3] = 0.0;
  2873.         m[1][3] = 0.0;
  2874.         m[2][3] = 0.0;
  2875.         m[3][3] = 1.0;
  2876.  
  2877. #if 0
  2878.         printf("Lookat matrix = \n");
  2879.         PrintMatrix(m);
  2880.  
  2881.         /*Now try to make quaternion*/
  2882.         MatrixToQuaternion(m, q);
  2883.         printf("Rotation quaternion = %g %g %g %g\n",
  2884.             q[0], q[1], q[2], q[3]);
  2885.         printf("Absolute value squared = %g\n",
  2886.             q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
  2887.         QInvert(q, qInv);
  2888.         printf("Inverted = %g %g %g %g\n",
  2889.             qInv[0], qInv[1], qInv[2], qInv[3]);
  2890.  
  2891.         qVec[0] = 0.0;
  2892.         qVec[1] = 0.0;
  2893.         qVec[2] = 0.0;
  2894.         qVec[3] = -1.0;
  2895.         QMult(qInv, qVec, qTest);
  2896.         QMult(qTest, q, qVec);
  2897.         printf("Rotated forward = %g %g %g %g\n",
  2898.             qVec[0], qVec[1], qVec[2], qVec[3]);
  2899.         qVec[0] = 0.0;
  2900.         qVec[1] = 0.0;
  2901.         qVec[2] = 1.0;
  2902.         qVec[3] = 0.0;
  2903.         QMult(qInv, qVec, qTest);
  2904.         QMult(qTest, q, qVec);
  2905.         printf("Rotated up = %g %g %g %g\n",
  2906.             qVec[0], qVec[1], qVec[2], qVec[3]);
  2907. #endif
  2908.     }
  2909.     }
  2910. }
  2911.  
  2912. #ifdef PROTO
  2913. Bool RotateLights(ObjPtr lights, real startAxis[3], real phi, Bool constrain, ObjPtr observer, ObjPtr space)
  2914. #else
  2915. Bool RotateLights(lights, axis, phi, constrain, observer, space)
  2916. ObjPtr lights;
  2917. real startAxis[3];
  2918. real phi;
  2919. Bool constrain;
  2920. ObjPtr observer;
  2921. ObjPtr space;
  2922. #endif
  2923. /*Rotates lights by rotDelta wrt observer within space*/
  2924. {
  2925.     ThingListPtr runner;
  2926.     Matrix rotDelta;
  2927.     real axis[3];
  2928.     float t;
  2929.     float sp, cp;        /*Sin and cosine of phi*/
  2930.  
  2931.     runner = LISTOF(lights);
  2932.     while (runner)
  2933.     {
  2934.     if (IsSelected(runner -> thing))
  2935.     {
  2936.         /*Rotate the light*/
  2937.         ObjPtr var;
  2938.         real location[3];
  2939.         real newLoc[3];
  2940.         float tv[3];
  2941.  
  2942.         axis[0] = startAxis[0];
  2943.         axis[1] = startAxis[1];
  2944.         axis[2] = startAxis[2];
  2945.  
  2946.         var = GetFixedArrayVar("DrawSpace", runner -> thing, LOCATION, 1, 3L);
  2947.         if (!var)
  2948.         {
  2949.         return false;
  2950.         }
  2951.         Array2CArray(location, var);
  2952.  
  2953.         /*Test elevation*/
  2954.         tv[0] = location[1];
  2955.         tv[1] = -location[0];
  2956.         tv[2] = 0.0;
  2957.    
  2958.         if (constrain)
  2959.         {
  2960.         float dot1, dot2;
  2961.         float rr;
  2962.  
  2963.         /*Constrain the axis to the azimuth or elevation*/
  2964.  
  2965.         /*Test azimuth*/
  2966.         dot1 = axis[2];
  2967.  
  2968.         /*Test elevation*/
  2969.         rr = sqrt(tv[0] * tv[0] + tv[1] * tv[1] + tv[2] * tv[2]);
  2970.         if (rr > 0.0)
  2971.         {
  2972.             rr = 1.0 / rr;
  2973.             tv[0] *= rr;
  2974.             tv[1] *= rr;
  2975.             tv[2] *= rr;
  2976.         }
  2977.         else
  2978.         {
  2979.             tv[0] = 1.0;
  2980.             tv[1] = 0.0;
  2981.             tv[2] = 0.0;
  2982.         }
  2983.  
  2984.         dot2 = axis[0] * tv[0] +
  2985.                axis[1] * tv[1] +
  2986.                axis[2] * tv[2];
  2987.  
  2988.         if (ABS(dot1) > ABS(dot2))
  2989.         {
  2990.             /*Azimuth is better*/
  2991.             axis[0] = 0.0;
  2992.             axis[1] = 0.0;
  2993.             axis[2] = 1.0;
  2994.             if (dot1 < 0.0)
  2995.             {
  2996.             axis[0] = -axis[0];
  2997.             axis[1] = -axis[1];
  2998.             axis[2] = -axis[2];
  2999.             }
  3000.         }
  3001.             else
  3002.         {
  3003.             axis[0] = tv[0];
  3004.             axis[1] = tv[1];
  3005.             axis[2] = tv[2];
  3006.             /*Elevation is better*/
  3007.             if (dot2 < 0.0)
  3008.             {
  3009.             axis[0] = -axis[0];
  3010.             axis[1] = -axis[1];
  3011.             axis[2] = -axis[2];
  3012.             }
  3013.         }
  3014.         }
  3015.  
  3016.         sp = rsin(phi);
  3017.         cp = rcos(phi);
  3018.         t = 1.0 - cp;
  3019.  
  3020.         /*Now make the change rotation matrix by rows from top to bottom*/
  3021.         rotDelta[0][0] = t * SQUARE(axis[0]) + cp;
  3022.         rotDelta[0][1] = t * axis[0] * axis[1] + sp * axis[2];
  3023.         rotDelta[0][2] = t * axis[0] * axis[2] - sp * axis[1];
  3024.         rotDelta[0][3] = 0.0;
  3025.  
  3026.         rotDelta[1][0] = t * axis[0] * axis[1] - sp * axis[2];
  3027.         rotDelta[1][1] = t * SQUARE(axis[1]) + cp;
  3028.         rotDelta[1][2] = t * axis[1] * axis[2] + sp * axis[0];
  3029.         rotDelta[1][3] = 0.0;
  3030.  
  3031.         rotDelta[2][0] = t * axis[0] * axis[2] + sp * axis[1];
  3032.         rotDelta[2][1] = t * axis[1] * axis[2] - sp * axis[0];
  3033.         rotDelta[2][2] = t * SQUARE(axis[2]) + cp;
  3034.         rotDelta[2][3] = 0.0;
  3035.  
  3036.         rotDelta[3][0] = 0.0;
  3037.         rotDelta[3][1] = 0.0;
  3038.         rotDelta[3][2] = 0.0;
  3039.         rotDelta[3][3] = 1.0;
  3040.  
  3041.         /*Rotate this location by rotDelta*/
  3042.         newLoc[0] = rotDelta[0][0] * location[0] +
  3043.             rotDelta[1][0] * location[1] +
  3044.             rotDelta[2][0] * location[2];
  3045.         newLoc[1] = rotDelta[0][1] * location[0] +
  3046.             rotDelta[1][1] * location[1] +
  3047.             rotDelta[2][1] * location[2];
  3048.         newLoc[2] = rotDelta[0][2] * location[0] +
  3049.             rotDelta[1][2] * location[1] +
  3050.             rotDelta[2][2] * location[2];
  3051.         location[0] = newLoc[0];
  3052.         location[1] = newLoc[1];
  3053.         location[2] = newLoc[2];
  3054.  
  3055.         NORM3(newLoc);
  3056.         var = NewRealArray(1, 3L);
  3057.         CArray2Array(var, newLoc);
  3058.         SetVar(runner -> thing, LOCATION, var);
  3059.         ImInvalid(runner -> thing);
  3060.     }
  3061.     runner = runner -> next;
  3062.     }
  3063.     return true;
  3064. }
  3065.  
  3066. int spaceDraw = 1;
  3067.  
  3068. ObjPtr SpacePanelBoundsInvalid(object, changeCount)
  3069. ObjPtr object;
  3070. unsigned long changeCount;
  3071. /*For a space panel, tests to see if it needs drawing.  Returns
  3072.   NULLOBJ    if it does not
  3073.   array[4]    giving bounds if it does
  3074.   ObjTrue    it it needs to be redrawn but does not know where
  3075.  
  3076.   Contents are assumed to be shifted in the bounds of owner
  3077. */
  3078. {
  3079.     ObjPtr contents;
  3080.     ObjPtr myBounds;
  3081.     real boundsElements[4];
  3082.  
  3083.     MakeVar(object, APPEARANCE);
  3084.  
  3085.     MakeVar(object, CHANGEDBOUNDS);
  3086.     if (GetVarChangeCount(object, CHANGEDBOUNDS) > changeCount)
  3087.     {
  3088.     /*Object is not good, so return the bounds*/
  3089.  
  3090.     myBounds = GetVar(object, CHANGEDBOUNDS);
  3091.     return myBounds;
  3092.     }
  3093.  
  3094.     myBounds = GetVar(object, BOUNDS);
  3095.  
  3096.     MakeVar(object, CONTENTS);
  3097.     contents = GetVar(object, CONTENTS);
  3098.     if (contents && IsList(contents))
  3099.     {
  3100.     /*Still, maybe some of the contents need to be drawn*/
  3101.     real testElements[4];
  3102.     real myBoundsElements[4];
  3103.      ObjPtr test;
  3104.      ThingListPtr runner;
  3105.  
  3106.     MakeVar(object, BOUNDS);
  3107.     myBounds = GetVar(object, BOUNDS);
  3108.     Array2CArray(myBoundsElements, myBounds);
  3109.  
  3110.     runner = LISTOF(contents);
  3111.     while (runner)
  3112.     {
  3113.         test = BoundsInvalid(runner -> thing, changeCount);
  3114.         if (test)
  3115.         {
  3116.         /*Hey, the kid needs redrawing*/
  3117.         return myBounds;
  3118.         }
  3119.         runner = runner -> next;
  3120.     }
  3121.     }
  3122.  
  3123.     return NULLOBJ;
  3124. }
  3125.  
  3126. ObjPtr SpaceBoundsInvalid(object, changeCount)
  3127. ObjPtr object;
  3128. unsigned long changeCount;
  3129. /*For a space, tests to see if it needs drawing.  Returns
  3130.   NULLOBJ    if it does not
  3131.   array[4]    giving bounds if it does
  3132.   ObjTrue    it it needs to be redrawn but does not know where
  3133. */
  3134. {
  3135.     ObjPtr contents;
  3136.     ObjPtr lights, observer, renderer;
  3137.     ObjPtr myBounds;
  3138.     real boundsElements[4];
  3139.     real testElements[4];
  3140.     ObjPtr test;
  3141.     real myBoundsElements[4];
  3142.     ObjPtr scrollBar;
  3143.     Bool firstTime = true;
  3144.     Bool doubleNoBounds = false;
  3145.     ObjPtr retVal = NULLOBJ;
  3146.     ObjPtr time;
  3147.  
  3148.     MakeVar(object, TIME);
  3149.     MakeVar(object, APPEARANCE);
  3150.     MakeVar(object, BOUNDS);
  3151.  
  3152.     myBounds = GetFixedArrayVar("SpaceBoundsInvalid", object, BOUNDS, 1, 4L);
  3153.     Array2CArray(myBoundsElements, myBounds);
  3154.  
  3155.     if (GetVarChangeCount(object, CHANGEDBOUNDS) > changeCount)
  3156.     {
  3157.     /*Object is not good, so return the bounds*/
  3158.     ObjPtr myBounds;
  3159.  
  3160.     MakeVar(object, CHANGEDBOUNDS);
  3161.     myBounds = GetVar(object, CHANGEDBOUNDS);
  3162.  
  3163.     if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1
  3164.         && DIMS(myBounds)[0] == 4)
  3165.     {
  3166.         retVal = myBounds;
  3167.     }
  3168.     else
  3169.     {
  3170.         retVal = ObjTrue;
  3171.     }
  3172.     }
  3173.  
  3174.     time = GetVar(object, TIME);
  3175.     if (time)
  3176.     {
  3177.     spaceTime = GetReal(time);
  3178.     }
  3179.     SetAllDatasetTimes(spaceTime);
  3180.  
  3181.     MakeVar(object, CONTENTS);
  3182.     contents = GetVar(object, CONTENTS);
  3183.     if (contents && IsList(contents))
  3184.     {
  3185.     /*Still, maybe some of the contents need to be drawn*/
  3186.      ThingListPtr runner;
  3187.  
  3188.     runner = LISTOF(contents);
  3189.     while (runner)
  3190.     {
  3191.         test = BoundsInvalid(runner -> thing, changeCount);
  3192.         if (test)
  3193.         {
  3194.         /*Hey, the kid needs redrawing*/
  3195.         retVal = myBounds;
  3196.         }
  3197.         runner = runner -> next;
  3198.     }
  3199.     }
  3200.  
  3201.     MakeVar(object, LIGHTS);
  3202.     lights = GetVar(object, LIGHTS);
  3203.     if (lights && IsList(lights))
  3204.     {
  3205.     /*Still, maybe some of the lights need to be drawn*/
  3206.      ThingListPtr runner;
  3207.  
  3208.     runner = LISTOF(lights);
  3209.     while (runner)
  3210.     {
  3211.         test = BoundsInvalid(runner -> thing, changeCount);
  3212.         if (test)
  3213.         {
  3214.         /*Hey, the light needs redrawing*/
  3215.         retVal =  myBounds;
  3216.         }
  3217.         runner = runner -> next;
  3218.     }
  3219.     }
  3220.  
  3221.     MakeVar(object, OBSERVER);
  3222.     observer = GetVar(object, OBSERVER);
  3223.     if (observer)
  3224.     {
  3225.     test = BoundsInvalid(observer, changeCount);
  3226.     if (test)
  3227.     {
  3228.         /*Hey, the observer needs redrawing*/
  3229.         retVal = myBounds;
  3230.     }
  3231.     }
  3232.  
  3233.     MakeVar(object, RENDERER);
  3234.     renderer = GetVar(object, RENDERER);
  3235.     if (renderer)
  3236.     {
  3237.     test = BoundsInvalid(renderer, changeCount);
  3238.     if (test)
  3239.     {
  3240.         /*Hey, the renderer needs redrawing*/
  3241.         retVal =  myBounds;
  3242.     }
  3243.     }
  3244.  
  3245.     return retVal;
  3246. }
  3247.  
  3248. ObjPtr DrawSpace(object)
  3249. ObjPtr object;
  3250. /*Draws a space and everything it contains*/
  3251. {
  3252. #ifdef GRAPHICS
  3253.     int left, right, bottom, top;
  3254.     ObjPtr eyePosnObj;            /*Eyeposition object*/
  3255.     ObjPtr tempObj;            /*Temporary object*/
  3256.     ObjPtr dRotMatrix;
  3257.     ObjPtr observer, observers;        /*Observer of the space*/
  3258.     ObjPtr panel;            /*Front and back panel*/
  3259.     ObjPtr var;                /*A random variable*/
  3260.     int renderType, filterType;        /*Types of renderers and filters*/
  3261.     int viewType;            /*Type of view*/
  3262.     int stereoMode;            /*Stereo mode*/
  3263.  
  3264. #if 0
  3265.     printf("DrawSpace %d\n", spaceDraw++);
  3266. #endif
  3267.  
  3268.     /*Get render type*/
  3269.     MakeVar(object, RENDERTYPE);
  3270.     var = GetIntVar("DrawSpace", object, RENDERTYPE);
  3271.     if (var)
  3272.     {
  3273.     renderType = GetInt(var);
  3274.     }
  3275.     else
  3276.     {
  3277.     renderType = hasRGB ? RT_RGB_HARDWARE : RT_CMAP_HARDWARE;
  3278.     }
  3279.     if (renderType == RT_NONE)
  3280.     {
  3281.     /*Don't render*/
  3282.     return ObjTrue;
  3283.     }
  3284.  
  3285.     /*Get filter type*/
  3286.     MakeVar(object, FILTERTYPE);
  3287.     var = GetIntVar("DrawSpace", object, FILTERTYPE);
  3288.     if (var)
  3289.     {
  3290.     filterType = GetInt(var);
  3291.     }
  3292.     else
  3293.     {
  3294.     filterType = FT_NONE;
  3295.     }
  3296.  
  3297.     Get2DIntBounds(object, &left, &right, &bottom, &top);
  3298.     if (IsDrawingRestricted(left, right, bottom, top)) return ObjFalse;
  3299.  
  3300.     /*Update the rotation matrix and fly through*/
  3301.     observer = GetObjectVar("DrawSpace", object, OBSERVER);
  3302.  
  3303.     MakeVar(observer, VIEWTYPE);
  3304.     var = GetVar(observer, VIEWTYPE);
  3305.     if (var)
  3306.     {
  3307.     viewType = GetInt(var);
  3308.     }
  3309.     else
  3310.     {
  3311.     viewType = VT_PERSPECTIVE;
  3312.     }
  3313.  
  3314.     MakeVar(observer, STEREOMODE);
  3315.     var = GetVar(observer, STEREOMODE);
  3316.     if (var)
  3317.     {
  3318.     stereoMode = GetInt(var);
  3319.     }
  3320.     else
  3321.     {
  3322.     stereoMode = SM_MONO;
  3323.     }
  3324.  
  3325.     if (drawingMode == DRAW_SCREEN)
  3326.     {
  3327.     switch(stereoMode)
  3328.     {
  3329.         case SM_MONO:
  3330.         DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3331.         break;
  3332.         case SM_CROSSEYED:
  3333.         DrawSpaceContents(object, left, right, bottom, top, VIEW_XLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3334.         DrawSpaceContents(object, left, right, bottom, top, VIEW_XRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3335.         break;
  3336.         case SM_EVENODD:
  3337.         DrawSpaceContents(object, left, right, bottom, top, VIEW_EOLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3338.         DrawSpaceContents(object, left, right, bottom, top, VIEW_EORIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3339.         break;
  3340.         case SM_ODDEVEN:
  3341.         DrawSpaceContents(object, left, right, bottom, top, VIEW_OELEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3342.         DrawSpaceContents(object, left, right, bottom, top, VIEW_OERIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3343.         break;
  3344.         case SM_WALLEYED:
  3345.         DrawSpaceContents(object, left, right, bottom, top, VIEW_WLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3346.         DrawSpaceContents(object, left, right, bottom, top, VIEW_WRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3347.         break;
  3348.         case SM_LEFTONTOP:
  3349.         DrawSpaceContents(object, left, right, bottom, top, VIEW_CLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3350.         DrawSpaceContents(object, left, right, bottom, top, VIEW_CRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3351.         break;
  3352.         case SM_REDCYAN:
  3353.         if (rgbp)
  3354.         {
  3355.             DrawSpaceContents(object, left, right, bottom, top, VIEW_RCLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3356.             DrawSpaceContents(object, left, right, bottom, top, VIEW_RCRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3357.         }
  3358.         else
  3359.         {
  3360.             DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3361.         }
  3362.         break;
  3363.         case SM_CYANRED:
  3364.         if (rgbp)
  3365.         {
  3366.             DrawSpaceContents(object, left, right, bottom, top, VIEW_CRLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3367.             DrawSpaceContents(object, left, right, bottom, top, VIEW_CRRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3368.         }
  3369.         else
  3370.         {
  3371.             DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3372.         }
  3373.         break;
  3374.         case SM_REDGREEN:
  3375.         if (rgbp)
  3376.         {
  3377.             DrawSpaceContents(object, left, right, bottom, top, VIEW_RGLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3378.             DrawSpaceContents(object, left, right, bottom, top, VIEW_RGRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3379.         }
  3380.         else
  3381.         {
  3382.             DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3383.         }
  3384.         break;
  3385.         case SM_GREENRED:
  3386.         if (rgbp)
  3387.         {
  3388.             DrawSpaceContents(object, left, right, bottom, top, VIEW_GRLEFT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3389.             DrawSpaceContents(object, left, right, bottom, top, VIEW_GRRIGHT | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3390.         }
  3391.         else
  3392.         {
  3393.             DrawSpaceContents(object, left, right, bottom, top, VIEW_CENTER | (viewType == VT_ORTHOGRAPHIC ? VIEW_MOD_ORTHO : 0));
  3394.         }
  3395.         break;
  3396.     }
  3397.  
  3398.     /*Filter space if need be*/
  3399.     if (rgbp && drawingQuality == DQ_FULL && filterType == FT_SHRINK)
  3400.     {
  3401.         /*Shrink the pixels in the window*/
  3402.         int s, d;
  3403.         register int y, x;
  3404.         int xdd, ydd;
  3405.         int xSize, ySize;
  3406.         Pixel *imageBuffer;
  3407.     
  3408.         if (right - left > SCRWIDTH) right = left + SCRWIDTH;
  3409.         if (top - bottom > SCRHEIGHT) top = bottom + SCRHEIGHT;
  3410.     
  3411.         if ((right - left) & 1) --right;
  3412.         if ((top - bottom) & 1) --top;
  3413.         xdd = (right - left) / 2;
  3414.         ydd = (top - bottom) / 2;
  3415.         xSize = (right - left);
  3416.         ySize = (top - bottom);
  3417.     
  3418.         /*Create an image buffer*/
  3419.         imageBuffer = (Pixel *) Alloc(SCRWIDTH * SCRHEIGHT * sizeof(Pixel));
  3420.         if (imageBuffer)
  3421.         {
  3422.         lrectread((Screencoord) left, (Screencoord) bottom, 
  3423.             (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) imageBuffer);
  3424.     
  3425.         s = 0;
  3426.         d = 0;
  3427.         for (y = 0; y <= ydd; ++y)
  3428.         {
  3429.             for (x = 0; x <= xdd; ++x)
  3430.             {
  3431.             imageBuffer[d] . red =
  3432.                 (imageBuffer[s] . red +
  3433.                  imageBuffer[s + 1] . red +
  3434.                  imageBuffer[s + xSize] . red +
  3435.                  imageBuffer[s + xSize + 1] . red) / 4;
  3436.             imageBuffer[d] . green =
  3437.                 (imageBuffer[s] . green +
  3438.                  imageBuffer[s + 1] . green +
  3439.                  imageBuffer[s + xSize] . green +
  3440.                  imageBuffer[s + xSize + 1] . green) / 4;
  3441.             imageBuffer[d] . blue =
  3442.                 (imageBuffer[s] . blue +
  3443.                  imageBuffer[s + 1] . blue +
  3444.                  imageBuffer[s + xSize] . blue +
  3445.                  imageBuffer[s + xSize + 1] . blue) / 4;
  3446.             imageBuffer[d] . alpha =
  3447.                 (imageBuffer[s] . alpha +
  3448.                  imageBuffer[s + 1] . alpha +
  3449.                  imageBuffer[s + xSize] . alpha +
  3450.                  imageBuffer[s + xSize + 1] . alpha) / 4;
  3451.             s += 2;
  3452.             ++d;
  3453.             }
  3454.             s += xSize;
  3455.         }
  3456.         EraseAll();
  3457.         lrectwrite((Screencoord) left, (Screencoord) bottom, 
  3458.             (Screencoord) left + xdd - 1, (Screencoord) bottom + ydd - 1, 
  3459.             (unsigned long *) imageBuffer);
  3460.         SAFEFREE(imageBuffer);
  3461.         }
  3462.     }
  3463.     if (rgbp && drawingQuality == DQ_FULL && filterType == FT_4AVERAGE)
  3464.     {
  3465.         /*Average the pixels in the window*/
  3466.         int s;
  3467.         register int y, x;
  3468.         int xSize, ySize;
  3469.         Pixel *imageBuffer;
  3470.     
  3471.         if (right - left > SCRWIDTH) right = left + SCRWIDTH;
  3472.         if (top - bottom > SCRHEIGHT) top = bottom + SCRHEIGHT;
  3473.     
  3474.         xSize = (right - left);
  3475.         ySize = (top - bottom);
  3476.     
  3477.         /*Create an image buffer*/
  3478.         imageBuffer = (Pixel *) Alloc(SCRWIDTH * SCRHEIGHT * sizeof(Pixel));
  3479.         if (imageBuffer)
  3480.         {
  3481.         lrectread((Screencoord) left, (Screencoord) bottom, 
  3482.             (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) imageBuffer);
  3483.     
  3484.         s = 0;
  3485.         for (y = 0; y <= ySize - 1; ++y)
  3486.         {
  3487.             for (x = 0; x <= xSize - 1; ++x)
  3488.             {
  3489.             imageBuffer[s] . red =
  3490.                 (imageBuffer[s] . red +
  3491.                  imageBuffer[s + 1] . red +
  3492.                  imageBuffer[s + xSize] . red +
  3493.                  imageBuffer[s + xSize + 1] . red) / 4;
  3494.             imageBuffer[s] . green =
  3495.                 (imageBuffer[s] . green +
  3496.                  imageBuffer[s + 1] . green +
  3497.                  imageBuffer[s + xSize] . green +
  3498.                  imageBuffer[s + xSize + 1] . green) / 4;
  3499.             imageBuffer[s] . blue =
  3500.                 (imageBuffer[s] . blue +
  3501.                  imageBuffer[s + 1] . blue +
  3502.                  imageBuffer[s + xSize] . blue +
  3503.                  imageBuffer[s + xSize + 1] . blue) / 4;
  3504.             imageBuffer[s] . alpha = 255;
  3505.             ++s;
  3506.             }
  3507.             ++s;
  3508.         }
  3509.         lrectwrite((Screencoord) left, (Screencoord) bottom, 
  3510.             (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) imageBuffer);
  3511.         SAFEFREE(imageBuffer);
  3512.         }
  3513.     }
  3514.     }
  3515.     else if (drawingMode == DRAW_POSTSCRIPT && psFile)
  3516.     {
  3517.     /*Just save the image to a PostScript file*/
  3518.     register int y, x;
  3519.     int xSize, ySize;
  3520.     Pixel *imageBuffer;
  3521.  
  3522.     if (right - left > SCRWIDTH) right = left + SCRWIDTH;
  3523.     if (top - bottom > SCRHEIGHT) top = bottom + SCRHEIGHT;
  3524.  
  3525.     xSize = (right - left);
  3526.     ySize = (top - bottom);
  3527.  
  3528.     /*Create an image buffer*/
  3529.     imageBuffer = (Pixel *) Alloc(xSize * ySize * sizeof(Pixel));
  3530.     if (imageBuffer)
  3531.     {
  3532.         lrectread((Screencoord) left, (Screencoord) bottom, 
  3533.             (Screencoord) right - 1, (Screencoord) top - 1, (unsigned long *) imageBuffer);
  3534.         SavePSImage(psFile, imageBuffer, left, right, bottom, top);
  3535.         SAFEFREE(imageBuffer);
  3536.     }
  3537.     }
  3538. #endif
  3539.     return ObjTrue;
  3540. }
  3541.  
  3542. static ObjPtr KeyDownSpace(object, key, flags)
  3543. ObjPtr object;
  3544. int key;
  3545. long flags;
  3546. /*Does a keydown in a space.  Returns
  3547.   true iff the press really was in the space.*/
  3548. {
  3549.     ObjPtr retVal, tool, var;
  3550.  
  3551.     /*Get the space tool*/
  3552.     tool = GetIntVar("KeyDownSpace", object, EDITTOOL);
  3553.  
  3554.     if (tool && GetInt(tool) == ST_FLYING)
  3555.     {
  3556.     ObjPtr observer;
  3557.  
  3558.     observer = GetObjectVar("KeyDownSpace", object, OBSERVER);
  3559.     if (!observer)
  3560.     {
  3561.         return ObjFalse;
  3562.     }
  3563.  
  3564.     switch(key)
  3565.     {
  3566.         case '0':
  3567.         SetVar(observer, AIRSPEED, NewReal(0.0));
  3568.         return ObjTrue;
  3569.         case FK_UP_ARROW:
  3570.         var = GetVar(observer, AIRSPEED);
  3571.         if (var)
  3572.         {
  3573.             SetVar(observer, AIRSPEED, NewReal(AIRSPEEDFACTOR + GetReal(var)));
  3574.         }
  3575.         else
  3576.         {
  3577.             SetVar(observer, AIRSPEED, NewReal(AIRSPEEDFACTOR));
  3578.         }
  3579.         return ObjTrue;
  3580.         case FK_DOWN_ARROW:
  3581.         var = GetVar(observer, AIRSPEED);
  3582.         if (var)
  3583.         {
  3584.             SetVar(observer, AIRSPEED, NewReal(GetReal(var) - AIRSPEEDFACTOR));
  3585.         }
  3586.         else
  3587.         {
  3588.             SetVar(observer, AIRSPEED, NewReal(-AIRSPEEDFACTOR));
  3589.         }
  3590.         return ObjTrue;
  3591.     }
  3592.     }
  3593.     return ObjFalse;
  3594. }
  3595.  
  3596. Bool AddObjToSpace(object, space, corral, loc, visType)
  3597. ObjPtr object;
  3598. ObjPtr space;
  3599. ObjPtr corral;
  3600. ObjPtr loc;
  3601. ObjPtr visType;
  3602. /*Adds object or a representation of it to space.
  3603.   If the class of object is
  3604.     iconClass    Finds a preferred visualization for REPOBJ and adds it
  3605.     visClass     Adds it directly
  3606.     fileClass     Whines
  3607.     otherwise    Finds a preferred visualization and adds it
  3608.  
  3609.   loc is a location to put the new icon, or NULLOBJ
  3610.   visType is the preferred visualization, or NULLOBJ to get the preferred
  3611. */
  3612. {
  3613.     ObjPtr repObj, icon, defaultIcon;
  3614.     ObjPtr name;
  3615.     ObjPtr contents;
  3616.     ObjPtr clock;
  3617.     FuncTyp AddControls;
  3618.     FuncTyp method;
  3619.     ObjPtr parents;
  3620.  
  3621.     /*If object is a template, make a new copy*/
  3622.     if (GetPredicate(object, TEMPLATEP))
  3623.     {
  3624.     FuncTyp method;
  3625.  
  3626.     method = GetMethodSurely("AddObjToSpace", object, CLONE);
  3627.     if (method)
  3628.     {
  3629.         object = (*method)(object);
  3630.     }
  3631.     SetVar(object, TEMPLATEP, ObjFalse);
  3632.     SetVar(object, SELECTED, ObjFalse);
  3633.     }
  3634.  
  3635.     if (IsIcon(object))
  3636.     {
  3637.     /*It's an icon; got to find a REPOBJ*/
  3638.     object = GetVar(object, REPOBJ);
  3639.     if (!object)
  3640.     {
  3641.         return false;
  3642.     }
  3643.     }
  3644.     if (IsFile(object))
  3645.     {
  3646.     /*It's a file.  Whine*/
  3647.     WarnUser(CW_CANNOTVISFILE);
  3648.     return false;
  3649.     }
  3650.     if (IsVisObj(object) && visType)
  3651.     {
  3652.     /*Go down to dataset level if visObject*/
  3653.     repObj = GetVar(object, MAINDATASET);
  3654.     if (!repObj)
  3655.     {
  3656.         repObj = GetVar(object, REPOBJ);
  3657.     }
  3658.     object = repObj;
  3659.     }
  3660.     if (!IsVisObj(object))
  3661.     {
  3662.     /*It's not a visualization yet.  Gotta find one*/
  3663.  
  3664.     /*Always apply some filters*/
  3665.     repObj = MainFilters(object);
  3666.  
  3667.     object = NewVis(repObj, visType);
  3668.     if (!object)
  3669.     {
  3670.         WarnUser(CW_CANNOTVISERROR);
  3671.         return false;
  3672.     }
  3673.     if (OptionDown())
  3674.     {
  3675.         SetVar(object, HIDDEN, ObjTrue);
  3676.     }
  3677.     }
  3678.     else
  3679.     {
  3680.     repObj = GetVar(object, MAINDATASET);
  3681.     if (!repObj)
  3682.     {
  3683.         repObj = GetVar(object, REPOBJ);
  3684.     }
  3685.     }
  3686.  
  3687.     contents = GetVar(space, CONTENTS);
  3688.     PrefixList(contents, object);
  3689.     parents = GetListVar("AddObjToSpace", object, PARENTS);
  3690.     if (parents)
  3691.     {
  3692.     PrefixList(parents, space);
  3693.     }
  3694.     else
  3695.     {
  3696.     SetVar(object, PARENT, space);
  3697.     }
  3698.     ImInvalid(object);
  3699.     SetVar(object, SPACE, space);
  3700.  
  3701.     /*Make an icon that represents the field and put it in the corral*/
  3702.     icon = NewVisIcon(object);
  3703.     if (icon)
  3704.     {
  3705.     SetVar(icon, ICONLOC, loc);    
  3706.     SetVar(icon, SPACE, space);
  3707.     if (OptionDown())
  3708.     {
  3709.         SetVar(object, HIDDEN, ObjTrue);
  3710.     }
  3711.     DropIconInCorral(corral, icon);
  3712.     }
  3713.  
  3714.     method = GetMethod(object, DROPPEDINSPACE);
  3715.     if (method)
  3716.     {
  3717.     (*method)(object, space);
  3718.     }
  3719.  
  3720.     /*Reinitialize the clock in the space*/
  3721.     clock = GetVar(space, CLOCK);
  3722.     if (clock)
  3723.     {
  3724.     ReinitController(clock);
  3725.     }
  3726.  
  3727.     return true;
  3728. }
  3729.  
  3730. Bool DeleteControllerFromSpace(controller, space, corral)
  3731. ObjPtr controller, space, corral;
  3732. /*Deletes a controller from a space and its icon from the corral*/
  3733. {
  3734.     ObjPtr spaces;
  3735.     ObjPtr contents;
  3736.     ThingListPtr list;
  3737.  
  3738.     spaces = GetListVar("DeleteControllerFromSpace", controller, SPACES);
  3739.     if (!spaces) return false;
  3740.  
  3741.     /*Remove the space reference from the controller*/
  3742.     if (0 == DeleteFromList(spaces, space)) return false;
  3743.     
  3744.     /*Remove the controller's icon from the corral*/
  3745.     contents = GetListVar("DeleteControllerFromSpace", corral, CONTENTS);
  3746.     list = LISTOF(contents);
  3747.     while (list)
  3748.     {
  3749.     ObjPtr foundController;
  3750.     foundController = GetVar(list -> thing, REPOBJ);
  3751.     if (foundController == controller)
  3752.     {
  3753.         DeleteFromList(contents, list -> thing);
  3754.         ImInvalid(corral);
  3755.         break;
  3756.     }
  3757.     list = list -> next;
  3758.     }
  3759.  
  3760.     return true;
  3761. }
  3762.  
  3763. static ObjPtr BindClockToSpace(clock, space)
  3764. ObjPtr clock, space;
  3765. /*Makes space know about clock*/
  3766. {
  3767.     SetVar(space, CLOCK, clock);
  3768.     return ObjTrue;
  3769. }
  3770.  
  3771. static ObjPtr BindObserverToSpace(observer, space)
  3772. ObjPtr observer, space;
  3773. /*Makes space know about observer*/
  3774. {
  3775.     SetVar(space, OBSERVER, observer);
  3776.     return ObjTrue;
  3777. }
  3778.  
  3779. static ObjPtr BindRendererToSpace(renderer, space)
  3780. ObjPtr renderer, space;
  3781. /*Makes space know about renderer*/
  3782. {
  3783.     SetVar(space, RENDERER, renderer);
  3784.     return ObjTrue;
  3785. }
  3786.  
  3787. Bool AddControllerToSpace(controller, space, corral, loc)
  3788. ObjPtr controller;
  3789. ObjPtr space;
  3790. ObjPtr corral;
  3791. ObjPtr loc;
  3792. /*Adds a space controller to space at loc and puts its icon in corral.
  3793. */
  3794. {
  3795.     ObjPtr repObj, icon, iconY;
  3796.     ObjPtr contents, spaces;
  3797.     ThingListPtr list;
  3798.     ObjPtr controllerClass;
  3799.     ObjPtr defaultIcon;
  3800.     ObjPtr name;
  3801.     FuncTyp method;
  3802.  
  3803.     if (GetPredicate(controller, ONEONLY) &&
  3804.     (controllerClass = GetVar(controller, CLASSID)) &&
  3805.     IsInt(controllerClass))
  3806.     {
  3807.     int cc;
  3808.  
  3809.     /*First see if there is already a controller there*/
  3810.     cc = GetInt(controllerClass);
  3811.     contents = GetListVar("AddControllerToSpace", corral, CONTENTS);
  3812.     if (!contents) return false;
  3813.     list = LISTOF(contents);
  3814.     while (list)
  3815.     {
  3816.         ObjPtr foundController;
  3817.         foundController = GetVar(list -> thing, REPOBJ);
  3818.         if (IsController(foundController) && IntVarEql(foundController, CLASSID, cc))
  3819.         {
  3820.         if (foundController == controller)
  3821.         {
  3822.             /*This is really the same controller.  Just move the icon*/
  3823.             if (loc)
  3824.             {
  3825.             SetVar(list -> thing, ICONLOC, loc);
  3826.             ImInvalid(list -> thing);
  3827.             }
  3828.             return true;
  3829.         }
  3830.         else
  3831.         {
  3832.             /*It's a new controller.  Delete the old one and fall through*/
  3833.             DeleteControllerFromSpace(foundController, space, corral);
  3834.             break;
  3835.         }
  3836.         }
  3837.         list = list -> next;
  3838.     }
  3839.     }
  3840.     /*Make an icon that represents the new controller and put it in the corral*/
  3841.     name = GetStringVar("AddControllerToSpace", controller, NAME);
  3842.     defaultIcon = GetObjectVar("AddControllerToSpace", controller, DEFAULTICON);
  3843.     if (defaultIcon)
  3844.     {
  3845.     icon = NewObject(defaultIcon, 0);
  3846.     SetVar(icon, NAME, name ? name : NewString("Controller"));
  3847.     SetVar(icon, ICONGREYED, GetVar(controller, HIDDEN));
  3848.     }
  3849.     else
  3850.     {
  3851.     icon = NewIcon(0, 0, ICONQUESTION,
  3852.            name ? GetString(name) : "Controller");
  3853.     SetVar(icon, HELPSTRING, 
  3854.         NewString("This icon represents a controller.  For some reason, \
  3855. the default icon could not be found, so the icon appears as a question mark.  \
  3856. Please report this as a bug in SciAn."));
  3857.     }
  3858.     SetVar(icon, SPACE, space);
  3859.     SetVar(icon, REPOBJ, controller);
  3860.     method = GetMethod(controller, BINDTOSPACE);
  3861.     if (method)
  3862.     {
  3863.     (*method)(controller, space);
  3864.     }
  3865.     SetVar(icon, ICONLOC, loc);
  3866.  
  3867.     /*Make the controller know about this space*/
  3868.     spaces = GetListVar("AddControllerToSpace", controller, SPACES);
  3869.     if (!spaces) return false;
  3870.     PrefixList(spaces, space);
  3871.  
  3872.     ImInvalid(controller);
  3873.     DropIconInCorral(corral, icon);
  3874.  
  3875.     return true;
  3876. }
  3877.  
  3878. ObjPtr NewSpace(left, right, bottom, top)
  3879. int left, right, bottom, top;
  3880. /*Makes a new Space with bounds left, right, bottom, top*/
  3881. {
  3882.     ObjPtr retVal;
  3883.  
  3884.     retVal = NewObject(spaceClass, 0);
  3885.     
  3886.     Set2DIntBounds(retVal, left, right, bottom, top);
  3887.     SetVar(retVal, CONTENTS, NewList());
  3888.  
  3889.     return retVal;
  3890. }
  3891.  
  3892. #ifdef PROTO
  3893. void PrintClock(char *s, char *f, real t)
  3894. #else
  3895. void PrintClock(s, f, t)
  3896. char *s;
  3897. char *f;
  3898. real t;
  3899. #endif
  3900. /*Prints t in seconds to a string s using format f*/
  3901. {
  3902.     while (*f)
  3903.     {
  3904.     if (*f == '%')
  3905.     {
  3906.         /*Format string*/
  3907.         ++f;
  3908.         if (*f == '%')
  3909.         {
  3910.         /*Just print out a %*/
  3911.         *s++ = *f++;
  3912.         }
  3913.         else
  3914.         {
  3915.         char *temp;    /*Pointer into tempStr to assemble*/
  3916.         real n;        /*Number to print*/
  3917.         long i;        /*Temporary integer*/
  3918.         /*It's a real format.  Start assembling*/
  3919.  
  3920.         temp = tempStr;
  3921.         *temp++ = '%';
  3922.  
  3923.         /*Skip over flag(s)*/
  3924.         while (*f == '-' || *f == '+' || *f == ' ' || *f == '#')
  3925.         {
  3926.             *temp++ = *f++;
  3927.         }
  3928.  
  3929.         /*Skip over first number*/
  3930.         while (*f >= '0' && *f <= '9')
  3931.         {
  3932.             *temp++ = *f++;
  3933.         }
  3934.  
  3935.         /*Skip over second number*/
  3936.         if (*f == '.')
  3937.         {
  3938.             *temp++ = *f++;
  3939.             while (*f >= '0' && *f <= '9')
  3940.             {
  3941.             *temp++ = *f++;
  3942.             }
  3943.         }
  3944.  
  3945.         /*Now see what the format is*/
  3946.         switch (*f)
  3947.         {
  3948.             case 'h':
  3949.             /*Hours, [1,12]*/
  3950.             n = floor(t / 3600.0);
  3951.             i = n / 12.0;
  3952.             n = n - i * 12;
  3953.             if (n < 1.00) n += 12.00;
  3954.             break;
  3955.             case 'H':
  3956.             /*Unrestricted hours*/
  3957.             n = t / 3600.0;
  3958.             break;
  3959.             case 'i':
  3960.             /*Hours, [0,24)*/
  3961.             n = floor(t / 3600.0);
  3962.             i = n / 24.0;
  3963.             n = n - i * 24;
  3964.             break;
  3965.             case 'a':
  3966.             /*am or pm*/
  3967.             n = t / 3600.0;
  3968.             i = n;
  3969.             i = i % 24;
  3970.             sprintf(s, "%s", i >= 12 ? "pm" : "am");
  3971.             ++f;
  3972.             goto dontsprintf;
  3973.             break;
  3974.             case 'A':
  3975.             /*AM or PM*/
  3976.             n = t / 3600.0;
  3977.             i = n / 24.0;
  3978.             i = i % 24;
  3979.             sprintf(s, "%s", i >= 12 ? "PM" : "AM");
  3980.             ++f;
  3981.             goto dontsprintf;
  3982.             break;
  3983.             case 'M':
  3984.             /*Unrestricted minutes*/
  3985.             n = t / 60.0;
  3986.             break;
  3987.             case 'm':
  3988.             /*Minutes, 0 to 59*/
  3989.             n = floor(t / 60.0);
  3990.             i = n / 60.0;
  3991.             n = n - i * 60;
  3992.             break;
  3993.             case 's':
  3994.             /*Seconds, 0 to 59*/
  3995.             i = t / 60.0;
  3996.             n = t - i * 60;
  3997.             n = floor(n);
  3998.             break;
  3999.             case 'S':
  4000.             /*Unrestricted seconds*/
  4001.             case 't':
  4002.             case 'T':
  4003.             /*Just a timestep*/
  4004.             n = t;
  4005.             break;
  4006.             case 'e':
  4007.             case 'E':
  4008.             case 'f':
  4009.             case 'F':
  4010.             case 'g':
  4011.             case 'G':
  4012.             *temp++ = *f++;
  4013.             *temp = 0;
  4014.             sprintf(s, tempStr, t);
  4015.             goto dontsprintf;
  4016.             break;
  4017.             case 'x':
  4018.             case 'X':
  4019.             case 'd':
  4020.             case 'D':
  4021.             *temp++ = *f++;
  4022.             *temp = 0;
  4023.             sprintf(s, tempStr, (int) t);
  4024.             goto dontsprintf;
  4025.             break;
  4026.             default:
  4027.             strcpy(s, "Bad format: ");
  4028.             while (*s) ++s;
  4029.             *s++ = *f++;
  4030.             *s = 0;
  4031.             goto dontsprintf;
  4032.         }
  4033.         ++f;
  4034.  
  4035.         /*Ready to print*/
  4036.         *temp++ = 'f';
  4037.         *temp = 0;
  4038.         sprintf(s, tempStr, n);
  4039. dontsprintf:
  4040.         while (*s)
  4041.         {
  4042.             ++s;
  4043.         }
  4044.         }
  4045.     }
  4046.     else
  4047.     {
  4048.         *s++ = *f++;
  4049.     }
  4050.     }
  4051.     *s = 0;
  4052. }
  4053.  
  4054. void ChangeClockForDataset(window)
  4055. WinInfoPtr window;
  4056. /*Changes a clock in window for globalDataset*/
  4057. {
  4058.     ObjPtr space, clock;
  4059.     ObjPtr timeBounds;
  4060.     real oldtb[2], tb[2];
  4061.     ObjPtr contents;
  4062.     ObjPtr repObj;
  4063.     ThingListPtr runner;
  4064.  
  4065.     space = FindSpace(window);
  4066.     if (!space)
  4067.     {
  4068.     ReportError("ChangeClockForDataset", "No space in window");
  4069.     return;
  4070.     }
  4071.  
  4072.     clock = GetObjectVar("ChangeClockForDataset", space, CLOCK);
  4073.     if (!clock)
  4074.     {
  4075.     return;
  4076.     }
  4077.  
  4078.     timeBounds = GetVar(clock, TIMEBOUNDS);
  4079.     if (timeBounds)
  4080.     {
  4081.     Array2CArray(oldtb, timeBounds);
  4082.     }
  4083.     else
  4084.     {
  4085.     oldtb[0] = oldtb[1] = missingData;
  4086.     }
  4087.  
  4088.     contents = GetListVar("ChangeClockForDataset", space, CONTENTS);
  4089.     if (!contents)
  4090.     {
  4091.     return;
  4092.     }
  4093.  
  4094.     tb[0] = tb[1] = missingData;
  4095.     runner = LISTOF(contents);
  4096.     while (runner)
  4097.     {
  4098.     repObj = GetVar(runner -> thing, MAINDATASET);
  4099.     if (!repObj) repObj = GetObjectVar("ChangeClockForDataset", runner -> thing, REPOBJ);
  4100.     if (repObj)
  4101.     {
  4102.         ObjPtr objTime;
  4103.         real otb[2];
  4104.         MakeVar(repObj, TIMEBOUNDS);
  4105.         objTime = GetVar(repObj, TIMEBOUNDS);
  4106.         if (objTime)
  4107.         {
  4108.         Array2CArray(otb, objTime);
  4109.         if (tb[0] == missingData)
  4110.         {
  4111.             tb[0] = otb[0];
  4112.         }
  4113.         else
  4114.         {
  4115.             tb[0] = MIN(tb[0], otb[0]);
  4116.         }
  4117.  
  4118.         if (tb[1] == missingData)
  4119.         {
  4120.             tb[1] = otb[1];
  4121.         }
  4122.         else
  4123.         {
  4124.             tb[1] = MAX(tb[1], otb[1]);
  4125.         }
  4126.         }
  4127.     }
  4128.     runner = runner -> next;
  4129.     }
  4130.  
  4131.     if (tb[1] != oldtb[1] || tb[0] != oldtb[0])
  4132.     {
  4133.     if (tb[1] != missingData && tb[0] != missingData)
  4134.     {
  4135.         timeBounds = NewRealArray(1, 2L);
  4136.         CArray2Array(timeBounds, tb);
  4137.         SetVar(clock, TIMEBOUNDS, timeBounds);
  4138.         ImInvalid(clock);
  4139.     }
  4140.     else
  4141.     {
  4142.         SetVar(clock, TIMEBOUNDS, NULLOBJ);
  4143.     }
  4144.     }
  4145. }
  4146.  
  4147. ObjPtr MakeClockTimeBounds(clock)
  4148. ObjPtr clock;
  4149. /*Makes a clock's time bounds for the first time*/
  4150. {
  4151.     ForAllVisWindows(ChangeClockForDataset);
  4152.     SetVar(clock, TIMEBOUNDS, GetVar(clock, TIMEBOUNDS));
  4153.     return ObjTrue;
  4154. }
  4155.  
  4156. static ObjPtr MakeSpaceFilterType(space)
  4157. ObjPtr space;
  4158. /*Makes a space's filter type*/
  4159. {
  4160.     ObjPtr renderer;
  4161.  
  4162.     renderer = GetObjectVar("MakeSpaceFilterType", space, RENDERER);
  4163.     if (!renderer)
  4164.     {
  4165.     return ObjFalse;
  4166.     }
  4167.     SetVar(space, FILTERTYPE, GetVar(renderer, FILTERTYPE));
  4168.     return ObjTrue;
  4169. }
  4170.  
  4171. static ObjPtr MakeSpaceRenderType(space)
  4172. ObjPtr space;
  4173. /*Makes a space's RENDERTYPE.  Returns ObjTrue if it had an effect,
  4174.   ObjFalse otherwise.*/
  4175. {
  4176.     ObjPtr renderer;
  4177.     ObjPtr spaceRenderType, renderType;
  4178.  
  4179.     renderer = GetObjectVar("MakeSpaceRenderType", space, RENDERER);
  4180.     if (!renderer)
  4181.     {
  4182.     return ObjFalse;
  4183.     }
  4184.  
  4185.     spaceRenderType = GetVar(space, RENDERTYPE);
  4186.     renderType = GetVar(renderer, RENDERTYPE);
  4187.  
  4188.     if (!Equal(spaceRenderType, renderType))
  4189.     {
  4190.      WinInfoPtr window;
  4191.     window = (WinInfoPtr) GetWindowVar("MakeSpaceRenderType", space, OWNERWINDOW);
  4192.     if (!window) return ObjTrue;
  4193.  
  4194.         switch(GetInt(renderType))
  4195.         {
  4196.         case RT_RGB_HARDWARE:
  4197.             DeferMessage((ObjPtr) window, SETTORGBMODE);
  4198.             break;
  4199.         case RT_CMAP_HARDWARE:
  4200.             DeferMessage((ObjPtr) window, SETTOCMAPMODE);
  4201.             break;
  4202.         }
  4203.     }
  4204.     SetVar(space, RENDERTYPE, renderType);
  4205.     return ObjTrue;
  4206. }
  4207.  
  4208. void ReinitController(controller)
  4209. ObjPtr controller;
  4210. /*Reinitializes a controller by sending it a REINIT message and then resolving
  4211.   it*/
  4212. {
  4213.     FuncTyp method;
  4214.     method = GetMethod(controller, REINIT);
  4215.     if (method)
  4216.     {
  4217.     (*method)(controller);
  4218.     }
  4219. }
  4220.  
  4221. int pdspSerialNum = 0;
  4222.  
  4223. static ObjPtr DeletePaletteDisplay(display)
  4224. ObjPtr display;
  4225. /*Deletes display*/
  4226. {
  4227.     return ObjTrue;
  4228. }
  4229.  
  4230. void AddPaletteToSpacePanel(palette, panel, space, x, y)
  4231. ObjPtr palette, panel, space;
  4232. int x, y;
  4233. /*Adds palette to panel.*/
  4234. {
  4235.     ObjPtr paletteDisplay;
  4236.  
  4237.     sprintf(tempStr, "Palette Display %d", ++pdspSerialNum); 
  4238.     paletteDisplay = NewPaletteDisplay(x - DSPPALETTEWIDTH / 2, x + DSPPALETTEWIDTH / 2, 
  4239.             y - DSPPALETTEHEIGHT / 2, y + DSPPALETTEHEIGHT / 2,
  4240.             tempStr, palette);
  4241.     PrefixList(GetVar(panel, CONTENTS), paletteDisplay);
  4242.     SetVar(paletteDisplay, PARENT, panel);
  4243.     SetMethod(paletteDisplay, DELETEICON, DeletePaletteDisplay);
  4244.     SetVar(paletteDisplay, STICKINESS, NewInt(FLOATINGLEFT + FLOATINGRIGHT + FLOATINGTOP + FLOATINGBOTTOM));
  4245.     SetTextFont(paletteDisplay, ANNOTFONT);
  4246.     SetTextSize(paletteDisplay, ANNOTFONTSIZE);
  4247. }
  4248. static ObjPtr DropInSpacePanel(panel, dropObj, x, y)
  4249. ObjPtr panel, dropObj;
  4250. int x, y;
  4251. /*Drops object in a panel beginning at x and y.  Returns
  4252.   true iff the drop really was in the panel.*/
  4253. {
  4254.     int left, right, bottom, top;
  4255.  
  4256.     Get2DIntBounds(panel, &left, &right, &bottom, &top);
  4257.  
  4258.     if (x >= left && x <= right && y >= bottom && y <= top)
  4259.     {
  4260.     /*Hey!  It really was a drop in the panel*/
  4261.     ObjPtr contents;
  4262.     ObjPtr firstIcon;
  4263.     ThingListPtr restIcons;
  4264.     ThingListPtr runner;
  4265.     ObjPtr repObj;
  4266.     ObjPtr space;
  4267.     ObjPtr iconLoc;
  4268.     real loc[2];
  4269.     int xDisp, yDisp;
  4270.  
  4271.     if (IsList(dropObj))
  4272.     {
  4273.         restIcons = LISTOF(dropObj);
  4274.         firstIcon = restIcons -> thing;
  4275.         restIcons = restIcons -> next;
  4276.     }
  4277.     else if (IsIcon(dropObj))
  4278.     {
  4279.         firstIcon = dropObj;
  4280.         restIcons = 0;
  4281.     }
  4282.     else
  4283.     {
  4284.         ReportError("DropInSpacePanel", "An object other than an icon was dropped");
  4285.         return ObjFalse;
  4286.     }
  4287.  
  4288.     space = GetObjectVar("DropInSpacePanel", panel, SPACE);
  4289.     if (!space) return ObjFalse;
  4290.  
  4291.     iconLoc = GetFixedArrayVar("DropInSpacePanel", firstIcon, ICONLOC, 1, 2L);
  4292.     if (!iconLoc)
  4293.     {
  4294.         return ObjFalse;
  4295.     }
  4296.     Array2CArray(loc, iconLoc);
  4297.  
  4298.         /*Setup the new viewport*/
  4299.     StartPanel(left, right, bottom, top);
  4300.  
  4301.  
  4302.         x -= left;
  4303.         y -= bottom;
  4304.  
  4305.     xDisp = x - loc[0];
  4306.     yDisp = y - loc[1];
  4307.  
  4308.     /*Drop first icon*/
  4309.     repObj = GetVar(firstIcon, REPOBJ);
  4310.     if (IsClock(repObj))
  4311.     {
  4312.     }
  4313.     else if (IsPalette(repObj))
  4314.     {
  4315.         AddPaletteToSpacePanel(repObj, panel, space, x, y);
  4316.     }
  4317.  
  4318.     /*Drop remaining icons*/
  4319.     runner = restIcons;
  4320.     while (runner)
  4321.     {
  4322.         repObj = GetVar(runner -> thing, REPOBJ);
  4323.         if (IsClock(repObj))
  4324.         {
  4325.         }
  4326.         else if (IsPalette(repObj))
  4327.         {
  4328.         iconLoc = GetFixedArrayVar("DropInSpacePanel", runner -> thing, ICONLOC, 1, 2L);
  4329.         if (!iconLoc) break;
  4330.         Array2CArray(loc, iconLoc);
  4331.         loc[0] += xDisp;
  4332.         loc[1] += yDisp;
  4333.         AddPaletteToSpacePanel(repObj, panel, space, (int) loc[0], (int) loc[1]);
  4334.         }
  4335.         runner = runner -> next;
  4336.     }
  4337.  
  4338.     StopPanel();
  4339.     return ObjTrue;
  4340.     }
  4341.     else
  4342.     {
  4343.     return ObjFalse;
  4344.     }
  4345. }
  4346.  
  4347. #ifdef PROTO
  4348. void SetClock(ObjPtr clock, real time)
  4349. #else
  4350. void SetClock(clock, time)
  4351. ObjPtr clock;
  4352. real time;
  4353. #endif
  4354. /*Sets clock to time*/
  4355. {
  4356.     SetVar(clock, TIME, NewReal(time));
  4357.     ImInvalid(clock);
  4358. }
  4359.  
  4360. static ObjPtr MarkClockTime(clock, lateness)
  4361. ObjPtr clock;
  4362. double lateness;
  4363. /*Marks time in a clock after a delay of lateness*/
  4364. {
  4365.     ObjPtr curTime;
  4366.     ObjPtr var;
  4367.     ObjPtr timeBounds;
  4368.     real deltaTime, deltaTimePer;
  4369.     int deltaTimeUnits, deltaTimePerUnits;
  4370.     real time;
  4371.     real tb[2];
  4372.     ObjPtr name;
  4373.     ObjPtr whichDialog;
  4374.     WinInfoPtr dialog;
  4375.     ObjPtr spaces;
  4376.     ThingListPtr list;
  4377.     Bool wrap;
  4378.     ObjPtr runSpeed, runControl;
  4379.  
  4380.     DoNotDisturb(clock, MARKTIME);
  4381.  
  4382.     runControl = GetVar(clock, RUNCONTROL);
  4383.  
  4384.     runSpeed = GetVar(clock, RUNSPEED);
  4385.     if (!runSpeed)
  4386.     {
  4387.     return ObjTrue;
  4388.     }
  4389.  
  4390.     /*Get the delta time and delta time per units*/
  4391.     var = GetVar(clock, DTIMEUNITS);
  4392.     if (var)
  4393.     {
  4394.     deltaTimeUnits = GetInt(var);
  4395.     }
  4396.     else
  4397.     {
  4398.     deltaTimeUnits = 0;
  4399.     }
  4400.  
  4401.     var = GetVar(clock, DTIMEPERUNITS);
  4402.     if (var)
  4403.     {
  4404.     deltaTimePerUnits = GetInt(var);
  4405.     }
  4406.     else
  4407.     {
  4408.     deltaTimePerUnits = 0;
  4409.     }
  4410.  
  4411.     MakeVar(clock, DTIME);
  4412.     var = GetVar(clock, DTIME);
  4413.     if (!var || !IsReal(var))
  4414.     {
  4415.     /*Obviously don't want to do anything*/
  4416.     if (runControl)
  4417.     {
  4418.         InhibitLogging(true);
  4419.         SetValue(runControl, NewInt(RC_STOP));
  4420.         InhibitLogging(false);
  4421.     }
  4422.     DoNotDisturb(clock, MARKTIME);
  4423.     return ObjTrue;
  4424.     }
  4425.     deltaTime = GetReal(var);
  4426.  
  4427.     var = GetVar(clock, DTIMEPER);
  4428.     if (var)
  4429.     {
  4430.     deltaTime /= GetReal(var);
  4431.     }
  4432.     if (deltaTimePerUnits)
  4433.     {
  4434.     deltaTime = deltaTime * fps;
  4435.     }
  4436.  
  4437.     wrap = GetPredicate(clock, CYCLECLOCK);
  4438.  
  4439.     if (deltaTime == 0.0)
  4440.     {
  4441.     return ObjTrue;
  4442.     }
  4443.  
  4444.     switch (GetInt(runSpeed))
  4445.     {
  4446.     case RC_STOP:
  4447.         return ObjTrue;
  4448.     case RC_FORWARD:
  4449.         break;
  4450.     case RC_REVERSE:
  4451.         deltaTime = -deltaTime;
  4452.         break;
  4453.     case RC_FAST_FORWARD:
  4454.         deltaTime = 3.0 * deltaTime;
  4455.         break;
  4456.     case RC_FAST_REVERSE:
  4457.         deltaTime = -3.0 * deltaTime;
  4458.         break;
  4459.     }
  4460.  
  4461.  
  4462.     if (lateness <= 0.0)
  4463.     {
  4464.     return ObjTrue;
  4465.     }
  4466.  
  4467.     MakeVar(clock, TIME);
  4468.     curTime = GetVar(clock, TIME);
  4469.     if (curTime)
  4470.     {
  4471.     time = GetReal(curTime);
  4472.     }
  4473.     else
  4474.     {
  4475.     time = 0.0;
  4476.     }
  4477.  
  4478.     /*Increment the time by the elapsed time*/
  4479.     if (deltaTimeUnits)
  4480.     {
  4481.     ObjPtr timeSteps;
  4482.     MakeVar(clock, TIMESTEPS);
  4483.     timeSteps = GetVar(clock, TIMESTEPS);
  4484.     if (timeSteps)
  4485.     {
  4486.         real index, newTime;
  4487.         index = SearchFuzzyReal(timeSteps, time);
  4488.  
  4489.         index += deltaTime * lateness;
  4490.  
  4491.         newTime = FuzzyRealIndex(timeSteps, index);
  4492.  
  4493.         if (newTime != time)
  4494.         {
  4495.         time = newTime;
  4496.         }
  4497.         else
  4498.         {
  4499.         if (runControl)
  4500.         {
  4501.             InhibitLogging(true);
  4502.             SetValue(runControl, NewInt(RC_STOP));
  4503.             InhibitLogging(false);
  4504.         }
  4505.         }
  4506.     }
  4507.     }
  4508.     else
  4509.     {
  4510.     time += deltaTime * lateness;
  4511.     }
  4512.  
  4513.     /*Check against time bounds*/
  4514.     MakeVar(clock, TIMEBOUNDS);
  4515.     timeBounds = GetVar(clock, TIMEBOUNDS);
  4516.     if (timeBounds && IsRealArray(timeBounds) && RANK(timeBounds) == 1 &&
  4517.     DIMS(timeBounds)[0] == 2)
  4518.     {
  4519.     Array2CArray(tb, timeBounds);
  4520.     }
  4521.     else
  4522.     {
  4523.     tb[0] = 0.0;
  4524.     tb[1] = 1.0;
  4525.     }
  4526.     if (deltaTime > 0.0 && time > tb[1])
  4527.     {
  4528.     if (wrap)
  4529.     {
  4530.         time = tb[0];
  4531.         /*Another wakeup call*/
  4532.         WakeMe(clock, MARKTIME, Clock() + 0.001);
  4533.     }
  4534.     else
  4535.     {
  4536.         time = tb[1];
  4537.         if (runControl)
  4538.         {
  4539.         InhibitLogging(true);
  4540.         SetValue(runControl, NewInt(RC_STOP));
  4541.         InhibitLogging(false);
  4542.         }
  4543.     }
  4544.     }
  4545.     else if (deltaTime < 0.0 && time < tb[0])
  4546.     {
  4547.     if (wrap)
  4548.     {
  4549.         time = tb[1];
  4550.         /*Another wakeup call*/
  4551.         WakeMe(clock, MARKTIME, Clock() + 0.001);
  4552.     }
  4553.     else
  4554.     {
  4555.         time = tb[0];
  4556.         if (runControl)
  4557.         {
  4558.         InhibitLogging(true);
  4559.         SetValue(runControl, NewInt(RC_STOP));
  4560.         InhibitLogging(false);
  4561.         }
  4562.     }
  4563.     }
  4564.     else
  4565.     {
  4566.     /*Another wakeup call*/
  4567.     WakeMe(clock, MARKTIME, Clock() + 0.001);
  4568.     }
  4569.  
  4570.     /*Now change the clock*/
  4571.     InhibitLogging(true);
  4572.     SetClock(clock, time);
  4573.     InhibitLogging(false);
  4574.  
  4575.     return ObjTrue;
  4576. }
  4577.  
  4578. #ifdef PROTO
  4579. void GetSliderRange(ObjPtr slider, real *loValue, real *hiValue)
  4580. #else
  4581. void GetSliderRange(slider, loValue, hiValue)
  4582. ObjPtr slider;
  4583. real *loValue, *hiValue;
  4584. #endif
  4585. /*Returns the range of slider into loValue and hiValue*/
  4586. {
  4587.     ObjPtr var;
  4588.     var = GetRealVar("GetSliderRange", slider, LOVALUE);
  4589.     if (var) *loValue = GetReal(var);
  4590.     var = GetRealVar("GetSliderRange", slider, HIVALUE);
  4591.     if (var) *hiValue = GetReal(var);
  4592. }
  4593.  
  4594.  
  4595.  
  4596. static ObjPtr ChangeRunControl(radioGroup)
  4597. ObjPtr radioGroup;
  4598. /*Changes the run control of a clock according to a newly pressed button*/
  4599. {
  4600.     ObjPtr clock;
  4601.     real loValue, hiValue;
  4602.     ObjPtr var;
  4603.     int value;
  4604.  
  4605.     var = GetValue(radioGroup);
  4606.     if (var) value = GetInt(var); else value = RC_STOP;
  4607.     
  4608.     clock = GetObjectVar("ChangeClockSpeed", radioGroup, REPOBJ);
  4609.     if (!clock) return ObjFalse;
  4610.  
  4611.     DoNotDisturb(clock, MARKTIME);
  4612.  
  4613.     SetVar(clock, RUNSPEED, NewInt(value));
  4614.  
  4615.     WakeMe(clock, MARKTIME, Clock() + 0.001);
  4616.  
  4617.     return ObjTrue;
  4618. }
  4619.  
  4620. static ObjPtr MakeClockTimeSteps(clock)
  4621. ObjPtr clock;
  4622. /*Makes a clock's TIMESTEPS variable*/
  4623. {
  4624.     long k;
  4625.     ObjPtr datasets;
  4626.     ObjPtr totalTimeSteps = NULLOBJ;
  4627.  
  4628.     MakeVar(clock, DATASETS);
  4629.     datasets = GetVar(clock, DATASETS);
  4630.     if (datasets)
  4631.     {
  4632.     for (k = 0; k < DIMS(datasets)[0]; ++k)
  4633.     {
  4634.         ObjPtr timeSteps;
  4635.         ObjPtr element;
  4636.  
  4637.         element = GetObjectElement(datasets, &k);
  4638.         MakeVar(element, TIMESTEPS);
  4639.         timeSteps = GetVar(element, TIMESTEPS);
  4640.         if (timeSteps)
  4641.         {
  4642.         if (!totalTimeSteps)
  4643.         {
  4644.             totalTimeSteps = timeSteps;
  4645.         }
  4646.         else
  4647.         {
  4648.             totalTimeSteps = MergeRealArrays(totalTimeSteps, timeSteps);
  4649.         }
  4650.         }
  4651.     }
  4652.     }
  4653.     totalTimeSteps = Uniq(totalTimeSteps);
  4654.     SetVar(clock, TIMESTEPS, totalTimeSteps);
  4655.     return ObjTrue;
  4656. }
  4657.  
  4658. static ObjPtr MakeClockDTime(clock)
  4659. ObjPtr clock;
  4660. /*Method to make a clock's DTIME*/
  4661. {
  4662.     real dt;
  4663.     ObjPtr datasets;
  4664.     Bool stepSet = false;
  4665.     real minTimeStep;
  4666.     long nTimeSteps = 0;
  4667.     long k;
  4668.  
  4669.         /*Calculate minimum time step*/
  4670.         MakeVar(clock, DATASETS);
  4671.         datasets = GetVar(clock, DATASETS);
  4672.         if (datasets)
  4673.         {
  4674.             ObjPtr element;
  4675.  
  4676.             for (k = 0; k < DIMS(datasets)[0]; ++k)
  4677.             {
  4678.                 ObjPtr timeSteps;
  4679.             element = GetObjectElement(datasets, &k);
  4680.  
  4681.                 MakeVar(element, TIMESTEPS);
  4682.                 timeSteps = GetVar(element, TIMESTEPS);
  4683.                 if (timeSteps)
  4684.             {
  4685.                 ObjPtr deltas;
  4686.                 deltas = SortArray(Uniq(RealArrayDeltas(timeSteps)));
  4687.  
  4688.                 /*Set the step only if there is one*/
  4689.                 nTimeSteps = DIMS(timeSteps)[0];
  4690.                 if (DIMS(timeSteps)[0] > 1)
  4691.                 {
  4692.                 if (stepSet)
  4693.                 {
  4694.                     minTimeStep = MIN(minTimeStep,
  4695.                       *((real *) ELEMENTS(deltas)));
  4696.                 }
  4697.                 else
  4698.                 {
  4699.                     minTimeStep = *((real *) ELEMENTS(deltas));
  4700.                     stepSet = true;
  4701.                 }
  4702.                 }
  4703.             }
  4704.             }
  4705.         }
  4706.         /*Estimate dt*/
  4707.         if (nTimeSteps > 1)
  4708.         {
  4709.             k = nTimeSteps / 20.0;
  4710.             if (k < 1) k = 1;
  4711.             else if (k > 10) k = 10;
  4712.             dt = minTimeStep * (real) k;
  4713.         }
  4714.         else
  4715.         {
  4716.             dt = 1.0;
  4717.         }
  4718.         SetVar(clock, DTIME, NewReal(dt));
  4719.  
  4720. }
  4721.  
  4722. static ObjPtr ShowClockControls(clock,  windowName)
  4723. ObjPtr clock;
  4724. ObjPtr windowName;
  4725. /*Makes a new clock window to control clock.  Ignores ownerWindow and windowName*/
  4726. {
  4727.     WinInfoPtr clockWindow;
  4728.     ObjPtr name;
  4729.     ObjPtr var;
  4730.     ObjPtr panel;
  4731.     ObjPtr contents;
  4732.     ObjPtr button, checkBox;
  4733.     int left;
  4734.     ObjPtr format;
  4735.     WinInfoPtr dialogExists;
  4736.     ObjPtr whichDialog;
  4737.  
  4738.     if (!clock) return NULLOBJ;
  4739.  
  4740.     name = GetVar(clock, NAME);
  4741.  
  4742.     whichDialog = NewString("Clock");
  4743.     dialogExists = DialogExists((WinInfoPtr) clock, whichDialog);
  4744.     if (name)
  4745.     {
  4746.     strncpy(tempStr, GetString(name), TEMPSTRSIZE);
  4747.     tempStr[TEMPSTRSIZE] = 0;
  4748.     }
  4749.     else
  4750.     {
  4751.     strcpy(tempStr, "Clock");
  4752.     }
  4753.     clockWindow = GetDialog((WinInfoPtr) clock, whichDialog, tempStr, 
  4754.     CLWINWIDTH, CLWINHEIGHT, CLWINWIDTH, CLWINHEIGHT, WINUI + WINFIXEDSIZE);
  4755.  
  4756.     if (!dialogExists)
  4757.     {
  4758.     int left, right, bottom, top;
  4759.  
  4760.     SetVar((ObjPtr) clockWindow, REPOBJ, clock);
  4761.  
  4762.     /*Put in a help string*/
  4763.     SetVar((ObjPtr) clockWindow, HELPSTRING,
  4764.         NewString("This window shows controls for a clock.  Using these \
  4765. controls, you can change the time displayed in all the spaces a clock controls \
  4766. or set time to advance forward or backward at a certain rate."));
  4767.  
  4768.     /*Add in a panel*/
  4769.     panel = NewPanel(greyPanelClass, 0, CLWINWIDTH, 0, CLWINHEIGHT);
  4770.     if (!panel)
  4771.     {
  4772.         return ObjFalse;
  4773.     }
  4774.     contents = GetVar((ObjPtr) clockWindow, CONTENTS);
  4775.     PrefixList(contents, panel);
  4776.     SetVar(panel, PARENT, (ObjPtr) clockWindow);
  4777.  
  4778.     contents = GetListVar("ShowClockControls", panel, CONTENTS);
  4779.     if (contents)
  4780.     {
  4781.         ObjPtr button, checkBox, radioGroup, control, titleBox;
  4782.         ObjPtr timeBounds;
  4783.         ObjPtr time;
  4784.         ObjPtr textBox;
  4785.         char readoutText[40];
  4786.         ObjPtr genericHelp;
  4787.         
  4788.         real dt, mdt;
  4789.         int left, right, bottom, top;
  4790.  
  4791.         left = MAJORBORDER;
  4792.         right = CLWINWIDTH - MAJORBORDER;
  4793.         top = CLWINHEIGHT - MAJORBORDER;
  4794.  
  4795.         MakeVar(clock, TIME);
  4796.         time = GetVar(clock, TIME);
  4797.  
  4798.         genericHelp = NewString("This controls the rate at which the clock \
  4799. advances through time.  When the forward play button is pushed, the clock will \
  4800. begin to advance forward the number of seconds or time steps given by the first \
  4801. text box for every number of seconds or frames given by the second time box.\n\
  4802. \n\
  4803. The easiest way to understand this is to read the text boxes and check boxes as \
  4804. a complete sentence.  Examples:\n\n\
  4805. \"Advance 0.25 second(s) every 1 second(s).\"\n\
  4806. This causes the clock to step forward at a rate of one-quarter second for every \
  4807. second that passes in the real world.  In other words, the clock will step at one-quarter \
  4808. real time.  When recording on videodisc, this will result in a one-quarter real time \
  4809. display when the videodisc is played back.\n\
  4810. \n\
  4811. \"Advance 5 time step(s) every 1 second(s).\"\n\
  4812. This causes the clock to step forward through five timesteps of the data for every \
  4813. second.  If the timesteps are unevenly spaced, the clock will speed up and slow down \
  4814. as neccessary to ensure that 5 timesteps are displayed every second.\n\
  4815. \n\
  4816. \"Advance 0.3 second(s) every 1 frame(s).\"\n\
  4817. This causes the clock to step forward three seconds for every frames.  A frame is \
  4818. the smallest unit of time that can be displayed given the limitations of the graphics \
  4819. device.  Videodiscs have a fixed frame rate of 30 frames per second (or 25 for PAL systems).  \
  4820. The interactive display has no fixed frame rate; it varies with how much time it takes to produce \
  4821. a picture.  \n\
  4822. \n\
  4823. To see one time step after another as fast as they can be displayed, set the controls to \
  4824. \"Advance 1 time step(s) every 1 frame(s).\"");
  4825.  
  4826.         /*Add a time control*/
  4827.         control = NewTimeControl(left, right, top - CLTCHEIGHT, top, "Time Control");
  4828.         PrefixList(contents, control);
  4829.         SetVar(control, PARENT, panel);
  4830.         AssocDirectControlWithVar(control, clock, TIME);
  4831.  
  4832.         ReinitController(clock);
  4833.  
  4834.         top -= CLTCHEIGHT + MINORBORDER;
  4835.  
  4836.         /*Add a title box*/
  4837.         left = MINORBORDER;
  4838.         bottom = MINORBORDER;
  4839.         right = CLWINWIDTH - MINORBORDER;
  4840.         titleBox = NewTitleBox(left, right, bottom, top, "Animation");
  4841.         PrefixList(contents, titleBox);
  4842.         SetVar(titleBox, PARENT, panel);
  4843.  
  4844.         top -= TITLEBOXTOP + MINORBORDER;
  4845.         left += MINORBORDER;
  4846.         right -= MINORBORDER;
  4847.  
  4848.         MakeVar(clock, DTIME);
  4849.         var = GetVar(clock, DTIME);
  4850.         if (var)
  4851.         {
  4852.         dt = GetReal(var);
  4853.         }
  4854.         else
  4855.         {
  4856.         SetVar(clock, DTIME, NewReal(0.0));
  4857.         printf("Shouldn't have to set clock's dtime\n");
  4858.         dt = 0.0;
  4859.         }
  4860.  
  4861.         bottom = top - EDITBOXHEIGHT;
  4862.         /*Create the Advance text box*/
  4863.         textBox = NewTextBox(left, left + (right - left) / 4,
  4864.                  bottom + (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN,
  4865.                  top - (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN,
  4866.                  PLAIN, "Advance legend", "Advance");
  4867.         SetVar(textBox, PARENT, panel);
  4868.         PrefixList(contents, textBox);
  4869.         SetTextAlign(textBox, CENTERALIGN);
  4870.  
  4871.         /*Create the dTime editable text box*/
  4872.         PrintNumber(readoutText, dt);
  4873.         textBox = NewTextBox(left + (right - left) / 4, left + 2 * (right - left) / 4,
  4874.                  bottom, top,
  4875.                  EDITABLE + WITH_PIT + ONE_LINE,
  4876.                  "Delta Time Box", readoutText);
  4877.         SetVar(textBox, PARENT, panel);
  4878.         PrefixList(contents, textBox);
  4879.         AssocTextRealControlWithVar(textBox, clock, DTIME, 0.0, plusInf, 0);
  4880.         SetVar(textBox, HELPSTRING, genericHelp);
  4881.  
  4882.         /*Create the seconds and timesteps box*/
  4883.         radioGroup = NewRadioButtonGroup("Delta Time Units");
  4884.         PrefixList(contents, radioGroup);
  4885.         SetVar(radioGroup, PARENT, panel);
  4886.  
  4887.         button = NewRadioButton(left + (right - left) / 4, left + 2 * (right - left) / 4,
  4888.                 bottom - CHECKBOXSPACING - CHECKBOXHEIGHT, bottom - CHECKBOXSPACING, "second(s)");
  4889.         AddRadioButton(radioGroup, button);
  4890.         button = NewRadioButton(left + (right - left) / 4, left + 2 * (right - left) / 4,
  4891.                 bottom - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, bottom - CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "time step(s)");
  4892.         AddRadioButton(radioGroup, button);
  4893.  
  4894.         var = GetVar(clock, DTIMEUNITS);
  4895.         if (!var) SetVar(clock, DTIMEUNITS, NewInt(0));
  4896.         AssocDirectControlWithVar(radioGroup, clock, DTIMEUNITS);
  4897.         SetVar(radioGroup, HELPSTRING, genericHelp);
  4898.  
  4899.         /*Create the Every text box*/
  4900.         textBox = NewTextBox(left + 2 * (right - left) / 4, left + 3 * (right - left) / 4,
  4901.                  bottom + (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN,
  4902.                  top - (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2 + EDITBOXDOWN,
  4903.                  PLAIN, "Every legend", "every");
  4904.         SetVar(textBox, PARENT, panel);
  4905.         PrefixList(contents, textBox);
  4906.         SetTextAlign(textBox, CENTERALIGN);
  4907.  
  4908.         /*Create the dTimePer editable text box*/
  4909.         var = GetVar(clock, DTIMEPER);
  4910.         if (!var)
  4911.         {
  4912.         SetVar(clock, DTIMEPER, NewReal(1.0));
  4913.         printf("Shouldn't have to set dtimeper\n");
  4914.         }
  4915.         PrintNumber(readoutText, var ? GetReal(var) : 1.0);
  4916.         textBox = NewTextBox(left + 3 * (right - left) / 4, right,
  4917.                  bottom, top,
  4918.                  EDITABLE + WITH_PIT + ONE_LINE,
  4919.                  "Delta Time Per Box", readoutText);
  4920.         SetVar(textBox, PARENT, panel);
  4921.         PrefixList(contents, textBox);
  4922.         AssocTextRealControlWithVar(textBox, clock, DTIMEPER, 0.0, plusInf, TR_NE_BOTTOM);
  4923.         SetVar(textBox, HELPSTRING, genericHelp);
  4924.  
  4925.         /*Create the seconds and frames box*/
  4926.         radioGroup = NewRadioButtonGroup("Delta Time Per");
  4927.         PrefixList(contents, radioGroup);
  4928.         SetVar(radioGroup, PARENT, panel);
  4929.         SetVar(radioGroup, HELPSTRING, genericHelp);
  4930.  
  4931.         button = NewRadioButton(left + 3 * (right - left) / 4, right,
  4932.                 bottom - CHECKBOXSPACING - CHECKBOXHEIGHT, bottom - CHECKBOXSPACING, "second(s)");
  4933.         AddRadioButton(radioGroup, button);
  4934.         button = NewRadioButton(left + 3 * (right - left) / 4, right,
  4935.                 bottom - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, bottom - CHECKBOXHEIGHT - 2 * CHECKBOXSPACING, "frame (s)");
  4936.         AddRadioButton(radioGroup, button);
  4937.  
  4938.         var = GetVar(clock, DTIMEPERUNITS);
  4939.         if (!var) SetVar(clock, DTIMEPERUNITS, NewInt(0));
  4940.         AssocDirectControlWithVar(radioGroup, clock, DTIMEPERUNITS);
  4941.  
  4942.         top = bottom - MAJORBORDER - 2 * CHECKBOXHEIGHT - 2 * CHECKBOXSPACING;
  4943.  
  4944.         /*Create the cycle time radio group*/
  4945.         radioGroup = NewRadioButtonGroup("Cycle the clock");
  4946.         SetVar(radioGroup, PARENT, panel);
  4947.         PrefixList(contents, radioGroup);
  4948.         AssocDirectControlWithVar(radioGroup, clock, CYCLECLOCK);
  4949.         SetVar(radioGroup, HELPSTRING,
  4950.         NewString("These radio buttons control whether the clock, when it is running forward \
  4951. or backward, stops at the endpoints in time or cycles around to the other end.\n"));
  4952.  
  4953.         /*And buttons*/
  4954.         button = NewRadioButton(left, (right + left) / 2,
  4955.                 top - CHECKBOXHEIGHT,
  4956.                 top,
  4957.                 "Stop at endpoints");
  4958.         SetVar(button, HELPSTRING,
  4959.         NewString("Click on this button to have the clock automatically stop when it reaches an endpoint."));
  4960.         AddRadioButton(radioGroup, button);
  4961.  
  4962.         button = NewRadioButton((right + left) / 2, right,
  4963.                 top - CHECKBOXHEIGHT,
  4964.                 top,
  4965.                 "Cycle at endpoints");
  4966.         SetVar(button, HELPSTRING,
  4967.         NewString("Click on this button to have the clock automatically cycle around it reaches an endpoint."));
  4968.         AddRadioButton(radioGroup, button);
  4969.  
  4970.         top -= MAJORBORDER + CHECKBOXHEIGHT;
  4971.  
  4972.         /*Create the icon buttons*/
  4973.         radioGroup = NewRadioButtonGroup("Speed Control");
  4974.         SetVar(radioGroup, PARENT, panel);
  4975.         SetVar(radioGroup, REPOBJ, clock);
  4976.         SetVar(radioGroup, HELPSTRING,
  4977.         NewString("These radio buttons control the speed of the clock.  \
  4978. They are tied to the Delta Time Per Second slider and provide easy access to some convenient values.  \
  4979. The most useful is Stop, the button in the center with the square icon."));
  4980.         PrefixList(contents, radioGroup); 
  4981.  
  4982.         /*Stop*/
  4983.         button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2,
  4984.                    (left + right) / 2 + ICONBUTTONSIZE / 2,
  4985.                    top - ICONBUTTONSIZE, top,
  4986.                    ICONSTOP, UIGREEN, "Stop", BS_PLAIN);
  4987.         SetVar(button, REPOBJ, clock);
  4988.         SetVar(button, HELPSTRING, NewString("This button stops the animation."));
  4989.         AddRadioButton(radioGroup, button);
  4990.  
  4991.         /*Forward*/
  4992.         button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 + ICONBUTTONSIZE + MINORBORDER,
  4993.                    (left + right) / 2 + ICONBUTTONSIZE / 2 + ICONBUTTONSIZE + MINORBORDER,
  4994.                    top - ICONBUTTONSIZE, top,
  4995.                    ICONPLAY, UIGREEN, "Play", BS_PLAIN);
  4996.         SetVar(button, REPOBJ, clock);
  4997.         SetVar(button, HELPSTRING, NewString("This button sets time moving forward at the rate specified above."));
  4998.         AddRadioButton(radioGroup, button);
  4999.  
  5000.         /*Reverse*/
  5001.         button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 - ICONBUTTONSIZE - MINORBORDER,
  5002.                    (left + right) / 2 + ICONBUTTONSIZE / 2 - ICONBUTTONSIZE - MINORBORDER,
  5003.                    top - ICONBUTTONSIZE, top,
  5004.                    ICONREV, UIGREEN, "Reverse", BS_PLAIN);
  5005.         SetVar(button, REPOBJ, clock);
  5006.         SetVar(button, HELPSTRING, NewString("This button sets time moving backward at the rate specified above."));
  5007.         AddRadioButton(radioGroup, button);
  5008.  
  5009.         /*Fast Forward*/
  5010.         button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 + 2 * (ICONBUTTONSIZE + MINORBORDER),
  5011.                    (left + right) / 2 + ICONBUTTONSIZE / 2 + 2 * (ICONBUTTONSIZE + MINORBORDER),
  5012.                    top - ICONBUTTONSIZE, top,
  5013.                    ICONFF, UIGREEN, "Fast Forward", BS_PLAIN);
  5014.         SetVar(button, REPOBJ, clock);
  5015.         SetVar(button, HELPSTRING, NewString("This button sets time moving forward at three times the rate specified above."));
  5016.         AddRadioButton(radioGroup, button);
  5017.  
  5018.         /*Fast Reverse*/
  5019.         button = NewIconButton((left + right) / 2 - ICONBUTTONSIZE / 2 - 2 * (ICONBUTTONSIZE + MINORBORDER),
  5020.                    (left + right) / 2 + ICONBUTTONSIZE / 2 - 2 * (ICONBUTTONSIZE + MINORBORDER),
  5021.                    top - ICONBUTTONSIZE, top,
  5022.                    ICONFR, UIGREEN, "Fast Reverse", BS_PLAIN);
  5023.         SetVar(button, REPOBJ, clock);
  5024.         SetVar(button, HELPSTRING, NewString("This button sets time moving backward at three times the rate specified above."));
  5025.         AddRadioButton(radioGroup, button);
  5026.  
  5027.         var = GetVar(clock, RUNSPEED);
  5028.         if (var)
  5029.         {
  5030.         SetValue(radioGroup, var);
  5031.         }
  5032.         else
  5033.         {
  5034.         SetValue(radioGroup, NewInt(RC_STOP));
  5035.         }
  5036.  
  5037.         SetMethod(radioGroup, CHANGEDVALUE, ChangeRunControl);
  5038.         SetVar(clock, RUNCONTROL, radioGroup);
  5039.     }
  5040.     }
  5041.  
  5042.     return (ObjPtr) clockWindow;
  5043. }
  5044.  
  5045. static ObjPtr MakePerspecControlAppearance(control)
  5046. ObjPtr control;
  5047. /*Makes a perspec control's appearance*/
  5048. {
  5049.     ObjPtr observer;
  5050.     FuncTyp method;
  5051.     ObjPtr var;
  5052.     int viewType;
  5053.     real perspecValue[4];
  5054.  
  5055.     /*Get the observer*/
  5056.     observer = GetObjectVar("MakePerspecControlAppearance", control, REPOBJ);
  5057.     if (!observer)
  5058.     {
  5059.     return ObjFalse;
  5060.     }
  5061.  
  5062.     var = GetVar(observer, FOCUSDIST);
  5063.     if (var)
  5064.     {
  5065.     perspecValue[0] = GetReal(var);
  5066.     }
  5067.     else
  5068.     {
  5069.     perspecValue[0] = INITEYEDIST;
  5070.     }
  5071.     var = GetVar(observer, VIEWFIELD);
  5072.     if (var)
  5073.     {
  5074.     perspecValue[1] = GetReal(var);
  5075.     }
  5076.     else
  5077.     {
  5078.     perspecValue[1] = INITAOV;
  5079.     }
  5080.     var = GetVar(observer, VIEWCLIP);
  5081.     if (var)
  5082.     {
  5083.     Array2CArray(&(perspecValue[2]), var);
  5084.     }
  5085.     else
  5086.     {
  5087.     perspecValue[2] = INITNEARCLIP;
  5088.     perspecValue[3] = INITFARCLIP;
  5089.     }
  5090.     if (perspecValue[2] < MINCLIP) perspecValue[2] = MINCLIP;
  5091.     if (perspecValue[3] < MINCLIP) perspecValue[3] = MINCLIP;
  5092.  
  5093.     var = NewRealArray(1, 4L);
  5094.     CArray2Array(var, perspecValue);
  5095.     method = GetMethod(control, CHANGEDVALUE);
  5096.     SetMethod(control, CHANGEDVALUE, (FuncTyp) 0);
  5097.     InhibitLogging(true);
  5098.     SetValue(control, var);
  5099.     InhibitLogging(false);
  5100.     SetMethod(control, CHANGEDVALUE, method);
  5101.  
  5102.     var = GetIntVar("ChangeViewType", observer, VIEWTYPE);
  5103.     if (!var)
  5104.     {
  5105.     return ObjFalse;
  5106.     }
  5107.     viewType = GetInt(var);
  5108.  
  5109.     if (viewType == VT_ORTHOGRAPHIC)
  5110.     {
  5111.     if (MakePerspecOrtho(control, true))
  5112.     {
  5113.         InhibitLogging(true);
  5114.         ChangedValue(control);
  5115.         InhibitLogging(false);
  5116.     }
  5117.     }
  5118.     else
  5119.     {
  5120.     if (MakePerspecOrtho(control, false))
  5121.     {
  5122.         InhibitLogging(true);
  5123.         ChangedValue(control);
  5124.         InhibitLogging(false);
  5125.     }
  5126.     }
  5127.  
  5128.     SetVar(control, APPEARANCE, ObjTrue);
  5129.  
  5130.     return ObjTrue;
  5131. }
  5132.  
  5133. static ObjPtr ChangePerspective(object)
  5134. ObjPtr object;
  5135. /*Change value for a perspective control*/
  5136. {
  5137.     ObjPtr val;
  5138.     ObjPtr repObj;
  5139.     real oldFocusDist;
  5140.     real newPerspec[4];
  5141.  
  5142.     repObj = GetObjectVar("ChangePerspective", object, REPOBJ);
  5143.     if (!repObj)
  5144.     {
  5145.     return ObjFalse;
  5146.     }
  5147.  
  5148.     val = GetRealVar("ChangePerspective", repObj, FOCUSDIST);
  5149.     if (!val)
  5150.     {
  5151.     return ObjFalse;
  5152.     }
  5153.     oldFocusDist = GetReal(val);
  5154.  
  5155.     val = GetFixedArrayVar("ChangePerspective", object, VALUE, 1, 4L);
  5156.     if (!val)
  5157.     {
  5158.     return ObjFalse;
  5159.     }
  5160.     Array2CArray(newPerspec, val);
  5161.  
  5162.     /*Change the observer's bits*/
  5163.     SetVar(repObj, VIEWFIELD, NewReal(newPerspec[1]));
  5164.     val = NewRealArray(1, 2L);
  5165.     CArray2Array(val, &(newPerspec[2]));
  5166.     SetVar(repObj, VIEWCLIP, val);
  5167.  
  5168.     /*See if field distance has changed*/
  5169.     if (oldFocusDist != newPerspec[0])
  5170.     {
  5171.     /*If so, change LOCATION accordingly*/
  5172.     real posn[3];
  5173.     real focus[3];
  5174.     real forward[3];
  5175.     ObjPtr var;
  5176.  
  5177.     GetFocusPoint(focus, repObj);
  5178.     GetObserverLocation(forward, repObj);
  5179.     forward[0] = focus[0] - forward[0];
  5180.     forward[1] = focus[1] - forward[1];
  5181.     forward[2] = focus[2] - forward[2];
  5182.     NORMALIZE(forward);
  5183.  
  5184.     posn[0] = focus[0] - forward[0] * newPerspec[0];
  5185.     posn[1] = focus[1] - forward[1] * newPerspec[0];
  5186.     posn[2] = focus[2] - forward[2] * newPerspec[0];
  5187.     SetVar(repObj, FOCUSDIST, NewReal(newPerspec[0]));
  5188.     var = NewRealArray(1, 3L);
  5189.     CArray2Array(var, posn);
  5190.     SetVar(repObj, LOCATION, var);
  5191.    }
  5192.  
  5193.    ImInvalid(repObj);
  5194.    return ObjTrue;
  5195. }
  5196.  
  5197. ObjPtr ChangeAirspeed(slider)
  5198. ObjPtr slider;
  5199. /*Changes the airspeed of an observer according to a slider*/
  5200. {
  5201.     ObjPtr controller;
  5202.     ObjPtr value;
  5203.  
  5204.     controller = GetObjectVar("ChangeAirspeed", slider, REPOBJ);
  5205.     if (!controller)
  5206.     {
  5207.     return ObjFalse;
  5208.     }
  5209.  
  5210.     value = GetRealVar("ChangeAirspeed", slider, VALUE);
  5211.     if (!value)
  5212.     {
  5213.     return ObjFalse;
  5214.     }
  5215.  
  5216.     SetVar(controller, AIRSPEED, value);
  5217.     ImInvalid(controller);
  5218.  
  5219.     return ObjTrue;
  5220. }
  5221.  
  5222. ObjPtr ChangeFlying(radio)
  5223. ObjPtr radio;
  5224. /*Changes whether an observer is flying based on radio*/
  5225. {
  5226.     ObjPtr repObj, var;
  5227.     Bool flying;
  5228.     repObj = GetObjectVar("ChangeFlying", radio, REPOBJ);
  5229.     if (!repObj)
  5230.     {
  5231.     return ObjFalse;
  5232.     }
  5233.     var = GetValue(radio);
  5234.     if (!var)
  5235.     {
  5236.     return ObjFalse;
  5237.     }
  5238.     flying = GetInt(var) ? true : false;
  5239.     if (flying)
  5240.     {
  5241.         WakeMe(repObj, MARKTIME, Clock() + 0.001);
  5242.     }
  5243.  
  5244.     SetVar(repObj, FLYING, flying ? ObjTrue : ObjFalse);
  5245.     ImInvalid(repObj);
  5246.     return ObjTrue;
  5247. }
  5248.  
  5249. ObjPtr ResetPosition(button)
  5250. ObjPtr button;
  5251. /*Resets an observer's position*/
  5252. {
  5253.     ObjPtr observer, var;
  5254.     real focusDist;
  5255.     real forward[3];
  5256.     real eyePosn[3];
  5257.     int i;
  5258.  
  5259.     observer = GetObjectVar("ResetPosition", button, REPOBJ);
  5260.     if (!observer)
  5261.     {
  5262.     return ObjFalse;
  5263.     }
  5264.  
  5265.     focusDist = INITEYEDIST;
  5266.     var = GetRealVar("ResetPosition", observer, FOCUSDIST);
  5267.     if (var)
  5268.     {
  5269.     focusDist = GetReal(var);
  5270.     }
  5271.  
  5272.     GetForwardVector(forward, observer);
  5273.  
  5274.     /*Derive the eye position from 0,0,0 through eye distance*/
  5275.     
  5276.     eyePosn[0] = -forward[0] * focusDist;
  5277.     eyePosn[1] = -forward[1] * focusDist;
  5278.     eyePosn[2] = -forward[2] * focusDist;
  5279.     var = NewRealArray(1, 3L);
  5280.     CArray2Array(var, eyePosn);
  5281.     SetVar(observer, LOCATION, var);
  5282.  
  5283.     ImInvalid(observer);
  5284.  
  5285.     return ObjTrue;
  5286. }
  5287.  
  5288. ObjPtr ResetRotation(button)
  5289. ObjPtr button;
  5290. /*Resets an observer's rotation*/
  5291. {
  5292.     ObjPtr observer;
  5293.     real focusPoint[3];
  5294.     real eyePosn[3];
  5295.     ObjPtr var;
  5296.     real focusDist;
  5297.     Quaternion rotQuat;
  5298.  
  5299.     observer = GetObjectVar("ResetRotation", button, REPOBJ);
  5300.     if (!observer)
  5301.     {
  5302.     return ObjFalse;
  5303.     }
  5304.  
  5305.     focusDist = INITEYEDIST;
  5306.     var = GetRealVar("ResetRotation", observer, FOCUSDIST);
  5307.     if (var)
  5308.     {
  5309.     focusDist = GetReal(var);
  5310.     }
  5311.  
  5312.     GetFocusPoint(focusPoint, observer);
  5313.  
  5314.     rotQuat[0] = 1.0;
  5315.     rotQuat[1] = 0.0;
  5316.     rotQuat[2] = 0.0;
  5317.     rotQuat[3] = 0.0;
  5318.     var = NewRealArray(1, 4L);
  5319.     CArray2Array(var, rotQuat);
  5320.     SetVar(observer, ROTQUAT, var);
  5321.  
  5322.     eyePosn[0] = focusPoint[0];
  5323.     eyePosn[1] = focusPoint[1];
  5324.     eyePosn[2] = focusPoint[2] + focusDist;
  5325.     var = NewRealArray(1, 3L);
  5326.     CArray2Array(var, eyePosn);
  5327.     SetVar(observer, LOCATION, var);
  5328.  
  5329.     ImInvalid(observer);
  5330.  
  5331.     return ObjTrue;
  5332. }
  5333.  
  5334. static ObjPtr ChangeObserverLookat(control)
  5335. ObjPtr control;
  5336. /*Changes an observers's lookat point*/
  5337. {
  5338.     ObjPtr repObj, var;
  5339.     NameTyp whichVar;
  5340.     long whichIndex;
  5341.     long nIndices;
  5342.     long k;
  5343.     real *el1, *el2;
  5344.     real minMax[2];
  5345.     real value;
  5346.     char *s;
  5347.     real forward[3], location[3], up[3], side[3], lookat[3], focusDist;
  5348.     real newRot[3][3];
  5349.     Quaternion rotQuat;
  5350.  
  5351.     repObj = GetVarSurely("ChangeObserverLookat", control, REPOBJ);
  5352.     if (!repObj)
  5353.     {
  5354.     return ObjFalse;
  5355.     }
  5356.  
  5357.     var = GetIntVar("ChangeObserverLookat", control, WHICHINDEX);
  5358.     if (!var)
  5359.     {
  5360.     return ObjFalse;
  5361.     }
  5362.     whichIndex = GetInt(var);
  5363.  
  5364.     var = GetRealVar("ChangeObserverLookat", repObj, FOCUSDIST);
  5365.     if (!var)
  5366.     {
  5367.     return ObjFalse;
  5368.     }
  5369.     focusDist = GetReal(var);
  5370.  
  5371.     GetForwardVector(forward, repObj);
  5372.     GetUpVector(up, repObj);
  5373.     GetObserverLocation(location, repObj);
  5374.  
  5375.     minMax[0] = minusInf;
  5376.     minMax[1] = plusInf;
  5377.  
  5378.     var = GetValue(control);
  5379.     if ((!var) || (!IsString(var)))
  5380.     {
  5381.     return ObjFalse;
  5382.     }
  5383.  
  5384.     s = GetString(var);
  5385.  
  5386.     if (ParseReal(&value, s))
  5387.     {
  5388.     if (value == missingData)
  5389.     {
  5390.         {
  5391.         WarnUser(CW_MISSINGERROR);
  5392.         return ObjFalse;
  5393.         }
  5394.     }
  5395.     else
  5396.     {
  5397.         {
  5398.         if (value <= minMax[0])
  5399.         {
  5400.             DeferMessage(control, RANGEALERT);
  5401.             return ObjFalse;
  5402.         }
  5403.         }
  5404.  
  5405.         {
  5406.         if (value >= minMax[1])
  5407.         {
  5408.             DeferMessage(control, RANGEALERT);
  5409.             return ObjFalse;
  5410.         }
  5411.         }
  5412.     }
  5413.  
  5414.     /*Dropped through, it must be OK*/
  5415.     if (GetPredicate(repObj, USESPACECOORDS))
  5416.     {
  5417.         real *elements;
  5418.         ObjPtr var;
  5419.  
  5420.         /*Have to convert into observer coords*/
  5421.  
  5422.         var = GetFixedArrayVar("MakeObserverLocationAppearance", repObj, SPACEORIGIN, 1, 3L);
  5423.         if (var)
  5424.         {
  5425.         elements = ELEMENTS(var);
  5426.         value -= elements[whichIndex];
  5427.         }
  5428.  
  5429.         var = GetRealVar("MakeObserverLocationAppearance", repObj, SPACESCALE);
  5430.         if (var)
  5431.         {
  5432.         value /= GetReal(var);
  5433.         }
  5434.     }
  5435.  
  5436.     }
  5437.     else
  5438.     {
  5439.     WarnUser(CW_NUMBERERROR);
  5440.     return ObjFalse;
  5441.     }
  5442.  
  5443.     /*Now use the new value to make a new lookat*/
  5444.     lookat[0] = location[0] + forward[0] * focusDist;
  5445.     lookat[1] = location[1] + forward[1] * focusDist;
  5446.     lookat[2] = location[2] + forward[2] * focusDist;
  5447.     lookat[whichIndex] = value;
  5448.  
  5449.     forward[0] = lookat[0] - location[0];
  5450.     forward[1] = lookat[1] - location[1];
  5451.     forward[2] = lookat[2] - location[2];
  5452.  
  5453.     focusDist = sqrt(SQUARE(forward[0]) + SQUARE(forward[1]) + SQUARE(forward[2]));
  5454.     if (focusDist < 0.0)
  5455.     {
  5456.     WarnUser(CW_LOOKATERROR);
  5457.     return ObjFalse;
  5458.     }
  5459.  
  5460.     /*Make a new forward, up, and distance*/
  5461.     forward[0] /= focusDist;
  5462.     forward[1] /= focusDist;
  5463.     forward[2] /= focusDist;
  5464.  
  5465.     CROSS(forward, up, side);
  5466.     if (side[0] == 0.0 && side[1] == 0.0 && side[2] == 0.0)
  5467.     {
  5468.     side[0] = forward[1];
  5469.     side[1] = forward[2];
  5470.     side[2] = forward[0];
  5471.     }
  5472.     NORMALIZE(side);
  5473.     CROSS(side, forward, up);
  5474.     NORMALIZE(up);
  5475.  
  5476.     /*Make a matrix*/
  5477.     newRot[0][0] = side[0];
  5478.     newRot[1][0] = side[1];
  5479.     newRot[2][0] = side[2];
  5480.  
  5481.     newRot[0][1] = up[0];
  5482.     newRot[1][1] = up[1];
  5483.     newRot[2][1] = up[2];
  5484.  
  5485.     newRot[0][2] = -forward[0];
  5486.     newRot[1][2] = -forward[1];
  5487.     newRot[2][2] = -forward[2];
  5488.  
  5489.     MatrixToQuaternion(newRot, rotQuat);
  5490.     var = NewRealArray(1, 4L);
  5491.     CArray2Array(var, rotQuat);
  5492.     SetVar(repObj, ROTQUAT, var);
  5493.  
  5494.     SetVar(repObj, FOCUSDIST, NewReal(focusDist));
  5495.  
  5496.     ImInvalid(repObj);
  5497.     SetVar(control, APPEARANCE, ObjTrue);
  5498.  
  5499.     return ObjTrue;
  5500. }
  5501.  
  5502. static ObjPtr MakeObserverLookatAppearance(control)
  5503. ObjPtr control;
  5504. /*Gets an observer's location and makes appearance*/
  5505. {
  5506.     real *location, forward[3], focusDist;
  5507.     ObjPtr repObj;
  5508.     ObjPtr var;
  5509.     real value;
  5510.     int whichAxis;
  5511.     FuncTyp method;
  5512.     real observerCoord;
  5513.  
  5514.     repObj = GetVar(control, REPOBJ);
  5515.     if (!repObj)
  5516.     {
  5517.     return ObjFalse;
  5518.     }
  5519.  
  5520.     MakeVar(repObj, LOCATION);
  5521.     var = GetFixedArrayVar("MakeObserverLookatAppearance", repObj, LOCATION, 1, 3L);
  5522.     if (!var)
  5523.     {
  5524.     return ObjFalse;
  5525.     }
  5526.     location = ELEMENTS(var);
  5527.  
  5528.     GetForwardVector(forward, repObj);
  5529.  
  5530.     MakeVar(repObj, FOCUSDIST);
  5531.     var = GetRealVar("MakeObserverLookatAppearance", repObj, FOCUSDIST);
  5532.     if (!var)
  5533.     {
  5534.     return ObjFalse;
  5535.     }
  5536.     focusDist = GetReal(var);
  5537.  
  5538.     var = GetIntVar("MakeObserverLookatAppearance", control, WHICHINDEX);
  5539.     if (var)
  5540.     {
  5541.     whichAxis = GetInt(var);
  5542.     }
  5543.     else
  5544.     {
  5545.     whichAxis = 0;
  5546.     }
  5547.  
  5548.     observerCoord = location[whichAxis] + forward[whichAxis] * focusDist;
  5549.  
  5550.     if (GetPredicate(repObj, USESPACECOORDS))
  5551.     {
  5552.     real *elements;
  5553.  
  5554.     /*Have to convert into space coords*/
  5555.     var = GetRealVar("MakeObserverLookatAppearance", repObj, SPACESCALE);
  5556.     if (var)
  5557.     {
  5558.         observerCoord *= GetReal(var);
  5559.     }
  5560.  
  5561.     var = GetFixedArrayVar("MakeObserverLookatAppearance", repObj, SPACEORIGIN, 1, 3L);
  5562.     if (var)
  5563.     {
  5564.         elements = ELEMENTS(var);
  5565.         observerCoord += elements[whichAxis];
  5566.     }
  5567.     }
  5568.  
  5569.     PrintNumber(tempStr, observerCoord);
  5570.     method = GetMethod(control, CHANGEDVALUE);
  5571.     SetMethod(control, CHANGEDVALUE, (FuncTyp) 0);
  5572.     InhibitLogging(true);
  5573.     SetValue(control, NewString(tempStr));
  5574.     InhibitLogging(false);
  5575.     SetMethod(control, CHANGEDVALUE, method);
  5576.  
  5577.     SetVar(control, APPEARANCE, ObjTrue);
  5578.     ImInvalid(control);
  5579.     return ObjTrue;
  5580. }
  5581.  
  5582. static ObjPtr ChangeObserverUp(control)
  5583. ObjPtr control;
  5584. /*Changes an observers's up vector */
  5585. {
  5586. }
  5587.  
  5588. static ObjPtr MakeObserverUpAppearance(control)
  5589. ObjPtr control;
  5590. /*Gets an observer's up and makes appearance*/
  5591. {
  5592.     real *elements;
  5593.     ObjPtr repObj;
  5594.     ObjPtr var;
  5595.     real value;
  5596.     int whichAxis;
  5597.     FuncTyp method;
  5598.  
  5599.     repObj = GetVar(control, REPOBJ);
  5600.     if (!repObj)
  5601.     {
  5602.     return ObjFalse;
  5603.     }
  5604.  
  5605.     MakeVar(repObj, UPVECTOR);
  5606.     var = GetFixedArrayVar("MakeObserverUpAppearance", repObj, UPVECTOR, 1, 3L);
  5607.     if (!var)
  5608.     {
  5609.     return ObjFalse;
  5610.     }
  5611.  
  5612.     elements = ELEMENTS(var);
  5613.  
  5614.     var = GetIntVar("MakeObserverUpAppearance", control, WHICHINDEX);
  5615.     if (var)
  5616.     {
  5617.     whichAxis = GetInt(var);
  5618.     }
  5619.     else
  5620.     {
  5621.     whichAxis = 0;
  5622.     }
  5623.  
  5624.     PrintNumber(tempStr, elements[whichAxis]);
  5625.     method = GetMethod(control, CHANGEDVALUE);
  5626.     SetMethod(control, CHANGEDVALUE, (FuncTyp) 0);
  5627.     InhibitLogging(true);
  5628.     SetValue(control, NewString(tempStr));
  5629.     InhibitLogging(false);
  5630.     SetMethod(control, CHANGEDVALUE, method);
  5631.  
  5632.     SetVar(control, APPEARANCE, ObjTrue);
  5633.     ImInvalid(control);
  5634.     return ObjTrue;
  5635. }
  5636.  
  5637. static ObjPtr ChangeObserverQuaternion(control)
  5638. ObjPtr control;
  5639. /*Changes an observers's quaternion */
  5640. {
  5641.     ObjPtr repObj;
  5642.     ObjPtr var;
  5643.     Quaternion oldQuat, newQuat;
  5644.     real value;
  5645.     int whichAxis;
  5646.     int prevAxis, nextAxis;
  5647.     real sign;
  5648.     real magnitude, desMag, mult;
  5649.     char *s;
  5650.  
  5651.     repObj = GetVar(control, REPOBJ);
  5652.     if (!repObj)
  5653.     {
  5654.     return ObjFalse;
  5655.     }
  5656.  
  5657.     MakeVar(repObj, ROTQUAT);
  5658.     var = GetFixedArrayVar("MakeObserverQuaternionAppearance", repObj, ROTQUAT, 1, 4L);
  5659.     if (!var)
  5660.     {
  5661.     return ObjFalse;
  5662.     }
  5663.     Array2CArray(oldQuat, var);
  5664.  
  5665.     var = GetIntVar("MakeObserverQuaternionAppearance", control, WHICHINDEX);
  5666.     if (var)
  5667.     {
  5668.     whichAxis = GetInt(var);
  5669.     }
  5670.     else
  5671.     {
  5672.     whichAxis = 0;
  5673.     }
  5674.  
  5675.     var = GetValue(control);
  5676.     if (!var)
  5677.     {
  5678.     return ObjFalse;
  5679.     }
  5680.     s = GetString(var);
  5681.     if (!ParseReal(&value, s))
  5682.     {
  5683.     return ObjFalse;
  5684.     }
  5685.     if (value <= minusInf || value >= plusInf)
  5686.     {
  5687.     WarnUser(CW_INFINITYERROR);
  5688.     return ObjFalse;
  5689.     }
  5690.     if (value == missingData)
  5691.     {
  5692.     WarnUser(CW_MISSINGERROR);
  5693.     return ObjFalse;
  5694.     }
  5695.     if (value > 1.0) value = 1.0;
  5696.     if (value < -1.0) value = -1.0;
  5697.  
  5698.     if (whichAxis == 0)
  5699.     {
  5700.     /*Just change amount of rotation, keep axis the same*/
  5701.     newQuat[0] = value;
  5702.     magnitude = sqrt(SQUARE(oldQuat[1]) + SQUARE(oldQuat[2]) + SQUARE(oldQuat[3]));
  5703.     if (magnitude > 0.0)
  5704.     {
  5705.         /*Have to adjust magnitude*/
  5706.         desMag = sqrt(1.0 - SQUARE(newQuat[0]));
  5707.         mult = desMag / magnitude;
  5708.  
  5709.         newQuat[1] = oldQuat[1] * mult;
  5710.         newQuat[2] = oldQuat[2] * mult;
  5711.         newQuat[3] = oldQuat[3] * mult;
  5712.     }
  5713.     else
  5714.     {
  5715.         /*Pick an arbitrary axis*/
  5716.         newQuat[1] = sqrt(1.0 - SQUARE(newQuat[0]));
  5717.         newQuat[2] = 0.0;
  5718.         newQuat[3] = 0.0;
  5719.     }
  5720.     }
  5721.     else
  5722.     {
  5723.     /*One of the vector components*/
  5724.     newQuat[0] = oldQuat[0];
  5725.     newQuat[1] = oldQuat[1];
  5726.     newQuat[2] = oldQuat[2];
  5727.     newQuat[3] = oldQuat[3];
  5728.  
  5729.     desMag = sqrt(1.0 - SQUARE(newQuat[0]));
  5730.     if (value > desMag) value = desMag;
  5731.     if (value < -desMag) value = -desMag;
  5732.     newQuat[whichAxis] = value;
  5733.  
  5734.     /*Try to fiddle just the next axis*/
  5735.     prevAxis = whichAxis - 1;
  5736.     if (prevAxis == 0) prevAxis = 3;
  5737.     nextAxis = whichAxis + 1;
  5738.     if (nextAxis == 4) nextAxis = 1;
  5739.  
  5740.     magnitude = sqrt(SQUARE(newQuat[whichAxis]) + SQUARE(newQuat[prevAxis]));
  5741.     if (magnitude <= desMag)
  5742.     {
  5743.         /*It can be done!*/
  5744.         sign = newQuat[nextAxis] >= 0.0 ? 1.0 : -1.0;
  5745.  
  5746.         newQuat[nextAxis] = sqrt(SQUARE(desMag) - SQUARE(magnitude)) * sign;
  5747.     }
  5748.     else
  5749.     {
  5750.         /*It can't be done.  Flatten to zero and make minimal adjustment
  5751.           to prevAxis*/
  5752.         newQuat[nextAxis] = 0.0;
  5753.         sign = newQuat[prevAxis] >= 0.0 ? 1.0 : -1.0;
  5754.         newQuat[prevAxis] = sqrt(SQUARE(desMag) - SQUARE(newQuat[whichAxis])) * sign;
  5755.     }
  5756.     }
  5757.  
  5758.     var = NewRealArray(1, 4L);
  5759.     CArray2Array(var, newQuat);
  5760.     SetVar(repObj, ROTQUAT, var);
  5761.     ImInvalid(repObj);
  5762.  
  5763.     return ObjTrue;
  5764. }
  5765.  
  5766. static ObjPtr MakeObserverQuaternionAppearance(control)
  5767. ObjPtr control;
  5768. /*Gets an observer's up and makes appearance*/
  5769. {
  5770.     real *elements;
  5771.     ObjPtr repObj;
  5772.     ObjPtr var;
  5773.     real value;
  5774.     int whichAxis;
  5775.     FuncTyp method;
  5776.  
  5777.     repObj = GetVar(control, REPOBJ);
  5778.     if (!repObj)
  5779.     {
  5780.     return ObjFalse;
  5781.     }
  5782.  
  5783.     MakeVar(repObj, ROTQUAT);
  5784.     var = GetFixedArrayVar("MakeObserverQuaternionAppearance", repObj, ROTQUAT, 1, 4L);
  5785.     if (!var)
  5786.     {
  5787.     return ObjFalse;
  5788.     }
  5789.  
  5790.     elements = ELEMENTS(var);
  5791.  
  5792.     var = GetIntVar("MakeObserverQuaternionAppearance", control, WHICHINDEX);
  5793.     if (var)
  5794.     {
  5795.     whichAxis = GetInt(var);
  5796.     }
  5797.     else
  5798.     {
  5799.     whichAxis = 0;
  5800.     }
  5801.  
  5802.     PrintNumber(tempStr, elements[whichAxis]);
  5803.     method = GetMethod(control, CHANGEDVALUE);
  5804.     SetMethod(control, CHANGEDVALUE, (FuncTyp) 0);
  5805.     InhibitLogging(true);
  5806.     SetValue(control, NewString(tempStr));
  5807.     InhibitLogging(false);
  5808.     SetMethod(control, CHANGEDVALUE, method);
  5809.  
  5810.     SetVar(control, APPEARANCE, ObjTrue);
  5811.     ImInvalid(control);
  5812.     return ObjTrue;
  5813. }
  5814.  
  5815. static ObjPtr HideOnAutoAdjust(control)
  5816. ObjPtr control;
  5817. /*Hides a control based on the AUTOADJUST of its repobj*/
  5818. {
  5819.     ObjPtr repObj;
  5820.  
  5821.     repObj = GetVar(control, REPOBJ);
  5822.     if (!repObj)
  5823.     {
  5824.     return ObjFalse;
  5825.     }
  5826.     ActivateTextBox(control, GetPredicate(repObj, AUTOADJUST) ? false : true);
  5827.  
  5828.     return ObjTrue;
  5829. }
  5830.  
  5831. static ObjPtr ChangeObserverLocation(control)
  5832. ObjPtr control;
  5833. /*Changes an observer's location value*/
  5834. {
  5835.     ObjPtr repObj, var;
  5836.     NameTyp whichVar;
  5837.     long whichIndex;
  5838.     long nIndices;
  5839.     long k;
  5840.     real *el1, *el2;
  5841.     real minMax[2];
  5842.     real value;
  5843.     char *s;
  5844.  
  5845.     repObj = GetVarSurely("ChangeObserverLocation", control, REPOBJ);
  5846.     if (!repObj)
  5847.     {
  5848.     return ObjFalse;
  5849.     }
  5850.  
  5851.     whichVar = LOCATION;
  5852.  
  5853.     var = GetIntVar("ChangeObserverLocation", control, WHICHINDEX);
  5854.     if (!var)
  5855.     {
  5856.     return ObjFalse;
  5857.     }
  5858.     whichIndex = GetInt(var);
  5859.  
  5860.     minMax[0] = minusInf;
  5861.     minMax[1] = plusInf;
  5862.  
  5863.     var = GetValue(control);
  5864.     if ((!var) || (!IsString(var)))
  5865.     {
  5866.     return ObjFalse;
  5867.     }
  5868.  
  5869.     s = GetString(var);
  5870.  
  5871.     if (ParseReal(&value, s))
  5872.     {
  5873.     if (value == missingData)
  5874.     {
  5875.         {
  5876.         WarnUser(CW_MISSINGERROR);
  5877.         return ObjFalse;
  5878.         }
  5879.     }
  5880.     else
  5881.     {
  5882.         {
  5883.         if (value <= minMax[0])
  5884.         {
  5885.             DeferMessage(control, RANGEALERT);
  5886.             return ObjFalse;
  5887.         }
  5888.         }
  5889.  
  5890.         {
  5891.         if (value >= minMax[1])
  5892.         {
  5893.             DeferMessage(control, RANGEALERT);
  5894.             return ObjFalse;
  5895.         }
  5896.         }
  5897.     }
  5898.  
  5899.     /*Dropped through, it must be OK*/
  5900.     var = GetArrayVar("ChangeObserverLocation", repObj, whichVar);
  5901.     if (!var)
  5902.     {
  5903.         return ObjFalse;
  5904.     }
  5905.     el1 = ELEMENTS(var);
  5906.     nIndices = DIMS(var)[0];
  5907.     var = NewRealArray(1, nIndices);
  5908.     el2 = ELEMENTS(var);
  5909.     for (k = 0; k < nIndices; ++k)
  5910.     {
  5911.         el2[k] = el1[k];
  5912.     }
  5913.     if (GetPredicate(repObj, USESPACECOORDS))
  5914.     {
  5915.         real *elements;
  5916.         ObjPtr var;
  5917.  
  5918.         /*Have to convert into observer coords*/
  5919.  
  5920.         var = GetFixedArrayVar("MakeObserverLocationAppearance", repObj, SPACEORIGIN, 1, 3L);
  5921.         if (var)
  5922.         {
  5923.         elements = ELEMENTS(var);
  5924.         value -= elements[whichIndex];
  5925.         }
  5926.  
  5927.         var = GetRealVar("MakeObserverLocationAppearance", repObj, SPACESCALE);
  5928.         if (var)
  5929.         {
  5930.         value /= GetReal(var);
  5931.         }
  5932.     }
  5933.  
  5934.     el2[whichIndex] = value;
  5935.     SetVar(repObj, whichVar, var);
  5936.  
  5937.     ImInvalid(repObj);
  5938.     }
  5939.     else
  5940.     {
  5941.     WarnUser(CW_NUMBERERROR);
  5942.     return ObjFalse;
  5943.     }
  5944.  
  5945.     SetVar(control, APPEARANCE, ObjTrue);
  5946.  
  5947.     return ObjTrue;
  5948. }
  5949.  
  5950. static ObjPtr MakeObserverLocationAppearance(control)
  5951. ObjPtr control;
  5952. /*Makes a text real control's appearance based on the variable it controls*/
  5953. {
  5954.     ObjPtr repObj, var, value;
  5955.     NameTyp whichVar;
  5956.     long whichIndex;
  5957.     FuncTyp method;
  5958.     real observerCoord;
  5959.  
  5960.     repObj = GetVarSurely("MakeObserverLocationAppearance", control, REPOBJ);
  5961.     if (!repObj)
  5962.     {
  5963.     return ObjFalse;
  5964.     }
  5965.  
  5966.     whichVar = LOCATION;
  5967.  
  5968.     var = GetIntVar("MakeObserverLocationAppearance", control, WHICHINDEX);
  5969.     if (!var)
  5970.     {
  5971.     return ObjFalse;
  5972.     }
  5973.     whichIndex = GetInt(var);
  5974.  
  5975.     var = GetArrayVar("MakeObserverLocationAppearance", repObj, whichVar);
  5976.     if (var)
  5977.     {
  5978.     real *elements;
  5979.     elements = ELEMENTS(var);
  5980.     observerCoord = elements[whichIndex];
  5981.     }
  5982.     else
  5983.     {
  5984.     observerCoord = 0.0;
  5985.     }
  5986.  
  5987.     if (GetPredicate(repObj, USESPACECOORDS))
  5988.     {
  5989.     real *elements;
  5990.  
  5991.     /*Have to convert into space coords*/
  5992.     var = GetRealVar("MakeObserverLocationAppearance", repObj, SPACESCALE);
  5993.     if (var)
  5994.     {
  5995.         observerCoord *= GetReal(var);
  5996.     }
  5997.  
  5998.     var = GetFixedArrayVar("MakeObserverLocationAppearance", repObj, SPACEORIGIN, 1, 3L);
  5999.     if (var)
  6000.     {
  6001.         elements = ELEMENTS(var);
  6002.         observerCoord += elements[whichIndex];
  6003.     }
  6004.     }
  6005.  
  6006.     PrintNumber(tempStr, observerCoord);
  6007.     InhibitLogging(true);
  6008.     method = GetMethod(control, CHANGEDVALUE);
  6009.     SetMethod(control, CHANGEDVALUE, (FuncTyp) 0);
  6010.     SetValue(control, NewString(tempStr));
  6011.     SetMethod(control, CHANGEDVALUE, method);
  6012.     method = GetMethod(control, EXTRAAPPEARANCE);
  6013.     if (method)
  6014.     {
  6015.     (*method)(control);
  6016.     }
  6017.     ImInvalid(control);
  6018.     InhibitLogging(false);
  6019.     SetVar(control, APPEARANCE, ObjTrue);
  6020.  
  6021.     return ObjTrue;
  6022. }
  6023.  
  6024. static ObjPtr AddViewControls(observer, panelContents)
  6025. ObjPtr observer, panelContents;
  6026. /*Adds observer controls to a panelContents*/
  6027. {
  6028.     ObjPtr textBox, titleBox, radio, button, control;
  6029.     ObjPtr var;
  6030.  
  6031.     /*View type chooser*/
  6032.     titleBox = TemplateTitleBox(ObserverTemplate, "View Type");
  6033.     SetVar(titleBox, PARENT, panelContents);
  6034.     PrefixList(panelContents, titleBox);
  6035.  
  6036.     radio = NewRadioButtonGroup("View Type Radio");
  6037.     PrefixList(panelContents, radio);
  6038.     SetVar(radio, PARENT, panelContents);
  6039.     SetVar(radio, REPOBJ, observer);
  6040.     SetVar(radio, HELPSTRING, NewString("This radio group controls \
  6041. the type of view given in all spaces controlled by this observer.  For more information \
  6042. about a given view type, use Help In Context on the button naming the view type.\n"));
  6043.  
  6044.     button = TemplateRadioButton(ObserverTemplate, "Perspective");
  6045.     AddRadioButton(radio, button);
  6046.     SetVar(button, HELPSTRING, NewString("When this button is down, the standard \
  6047. perspective view is used.  Objects closer to the observer appear larger, giving a \
  6048. realistic image.\n"));
  6049.  
  6050.     button = TemplateRadioButton(ObserverTemplate, "Orthographic");
  6051.     AddRadioButton(radio, button);
  6052.     SetVar(button, HELPSTRING, NewString("When this button is down, the \
  6053. orthograpic view is enabled.  Objects appear the same size no matter what the \
  6054. distance to the observer.  This view does not appear as realistic as the perspective \
  6055. view, but it does have the advantage that objects of the same size at different depths line up.  \
  6056. It is useful for viewing 2-D data or for comparing values in 3-D data at different \
  6057. depths.\n"));
  6058.  
  6059.     var = GetIntVar("AddViewControls", observer, VIEWTYPE);
  6060.     if (!var)
  6061.     {
  6062.     SetVar(observer, VIEWTYPE, NewInt(VT_PERSPECTIVE));
  6063.     }
  6064.     AssocDirectControlWithVar(radio, observer, VIEWTYPE);
  6065.  
  6066.     /*Add in the perspective control*/
  6067.         control = TemplatePerspecControl(ObserverTemplate, "Perspective Control");
  6068.     PrefixList(panelContents, control);
  6069.     SetVar(control, PARENT, panelContents);
  6070.     SetVar(control, REPOBJ, observer);
  6071.  
  6072.     SetMethod(control, CHANGEDVALUE, ChangePerspective);
  6073.     DeclareIndirectDependency(control, APPEARANCE, REPOBJ, VIEWCLIP);
  6074.     DeclareIndirectDependency(control, APPEARANCE, REPOBJ, VIEWFIELD);
  6075.     DeclareIndirectDependency(control, APPEARANCE, REPOBJ, FOCUSDIST);
  6076.     DeclareIndirectDependency(control, APPEARANCE, REPOBJ, VIEWTYPE);
  6077.     SetMethod(control, APPEARANCE, MakePerspecControlAppearance);
  6078.  
  6079.     /*Reset controls*/
  6080.     button = TemplateButton(ObserverTemplate, "Reset Position");
  6081.     PrefixList(panelContents, button);
  6082.     SetVar(button, PARENT, panelContents);
  6083.     SetVar(button, REPOBJ, observer);
  6084.     SetMethod(button, CHANGEDVALUE, ResetPosition);
  6085.     SetVar(radio, HELPSTRING, NewString("This button resets the position \
  6086. of the observer to the default.  It can be useful if you get lost.\n"));
  6087.  
  6088.     button = TemplateButton(ObserverTemplate, "Reset Rotation");
  6089.     PrefixList(panelContents, button);
  6090.     SetVar(button, PARENT, panelContents);
  6091.     SetVar(button, REPOBJ, observer);
  6092.     SetMethod(button, CHANGEDVALUE, ResetRotation);
  6093.     SetVar(radio, HELPSTRING, NewString("This button resets the rotation \
  6094. of the observer to the default.  It can be useful if you get lost.\n"));
  6095.     return ObjTrue;
  6096. }
  6097.  
  6098. static ObjPtr AddStereoControls(observer, panelContents)
  6099. ObjPtr observer, panelContents;
  6100. /*Adds stereo observer controls to a panelContents*/
  6101. {
  6102.     ObjPtr textBox, titleBox, radio, button, control;
  6103.     int l, r, b, t;
  6104.     ObjPtr var;
  6105.     ObjPtr icon;
  6106.  
  6107.     titleBox = TemplateTitleBox(StereoTemplate, "Stereo Image Presentation");
  6108.     PrefixList(panelContents, titleBox);
  6109.     SetVar(titleBox, PARENT, panelContents);
  6110.  
  6111.     radio = NewRadioButtonGroup("Stereo Image Radio");
  6112.     PrefixList(panelContents, radio);
  6113.     SetVar(radio, PARENT, panelContents);
  6114.     SetVar(radio, REPOBJ, observer);
  6115.     SetVar(radio, HELPSTRING, NewString("This radio group controls \
  6116. the method used to display in mono or stereo all spaces controlled by this observer.  For more information \
  6117. about a given view type, use Help In Context on the button naming the view type.\n"));
  6118.  
  6119.     button = TemplateRadioButton(StereoTemplate, "Mono");
  6120.     AddRadioButton(radio, button);
  6121.     SetVar(button, HELPSTRING, NewString("When this button is down, only one \
  6122. image appears in the space."));
  6123.  
  6124.     button = TemplateRadioButton(StereoTemplate, "Crosseyed");
  6125.     AddRadioButton(radio, button);
  6126.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6127. images are shown side by side for a stereo pair.  The image for the left eye is \
  6128. on the right, and the image for the right eye is on the left.  To view the stereo \
  6129. pair, cross your eyes until the images coincide.  For some people, this is very \
  6130. easy to do.  Some people have difficulty doing it.  Only try this if you personally \
  6131. find it easy, and if it becomes a strain, stop at once."));
  6132.  
  6133.     button = TemplateRadioButton(StereoTemplate, "Walleyed");
  6134.     AddRadioButton(radio, button);
  6135.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6136. images are shown side by side for a stereo pair.  The image for the left eye is \
  6137. on the left, and the image for the right eye is on the right.  To view the stereo \
  6138. pair, you will need to use a stereo viewer made of a pair of small periscopes.  See the \
  6139. SciAn technical notes for more details."));
  6140.  
  6141.     button = TemplateRadioButton(StereoTemplate, "Red/Cyan");
  6142.     AddRadioButton(radio, button);
  6143.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6144. perspective images are shown, one red and one cyan, for a stereo pair.  To view the stereo \
  6145. pair, you will need to use a pair of red/cyan 3-D glasses, with the red lens over \
  6146. the left eye.  Because one eye \
  6147. can see only one set of colors, subtle color variations will be washed out.  \
  6148. Also, colors which are heavily toward red or heavily toward blue will be confusing."));
  6149.     GetTemplateBounds(StereoTemplate, "Red/Cyan", &l, &r, &b, &t);
  6150.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONLGLASSES, (char *) 0);
  6151.     SetVar(icon, BACKGROUND, NewInt(UIRED));
  6152.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6153.     PrefixList(panelContents, icon);
  6154.     SetVar(icon, PARENT, panelContents);
  6155.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONRGLASSES, (char *) 0);
  6156.     SetVar(icon, BACKGROUND, NewInt(UICYAN));
  6157.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6158.     PrefixList(panelContents, icon);
  6159.     SetVar(icon, PARENT, panelContents);
  6160.  
  6161.     button = TemplateRadioButton(StereoTemplate, "Cyan/Red");
  6162.     AddRadioButton(radio, button);
  6163.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6164. perspective images are shown, one red and one cyan, for a stereo pair.  To view the stereo \
  6165. pair, you will need to use a pair of red/cyan 3-D glasses, with the red lens over \
  6166. the right eye.  Because one eye \
  6167. can see only one set of colors, subtle color variations will be washed out.  \
  6168. Also, colors which are heavily toward red or heavily toward blue will be confusing."));
  6169.     GetTemplateBounds(StereoTemplate, "Cyan/Red", &l, &r, &b, &t);
  6170.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONLGLASSES, (char *) 0);
  6171.     SetVar(icon, BACKGROUND, NewInt(UICYAN));
  6172.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6173.     PrefixList(panelContents, icon);
  6174.     SetVar(icon, PARENT, panelContents);
  6175.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONRGLASSES, (char *) 0);
  6176.     SetVar(icon, BACKGROUND, NewInt(UIRED));
  6177.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6178.     PrefixList(panelContents, icon);
  6179.     SetVar(icon, PARENT, panelContents);
  6180.  
  6181.     button = TemplateRadioButton(StereoTemplate, "Red/Green");
  6182.     AddRadioButton(radio, button);
  6183.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6184. perspective images are shown, one red and one cyan, for a stereo pair.  To view the stereo \
  6185. pair, you will need to use a pair of red/green 3-D glasses, with the red lens over \
  6186. the left eye.  This kind of viewing is even worse than Red/Cyan for color representation and \
  6187. can effectively be used only for monochrome images."));
  6188.     GetTemplateBounds(StereoTemplate, "Red/Green", &l, &r, &b, &t);
  6189.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONLGLASSES, (char *) 0);
  6190.     SetVar(icon, BACKGROUND, NewInt(UIRED));
  6191.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6192.     PrefixList(panelContents, icon);
  6193.     SetVar(icon, PARENT, panelContents);
  6194.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONRGLASSES, (char *) 0);
  6195.     SetVar(icon, BACKGROUND, NewInt(UIGREEN));
  6196.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6197.     PrefixList(panelContents, icon);
  6198.     SetVar(icon, PARENT, panelContents);
  6199.  
  6200.     button = TemplateRadioButton(StereoTemplate, "Green/Red");
  6201.     AddRadioButton(radio, button);
  6202.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6203. perspective images are shown, one red and one cyan, for a stereo pair.  To view the stereo \
  6204. pair, you will need to use a pair of red/green 3-D glasses, with the red lens over \
  6205. the right eye.  This kind of viewing is even worse than Red/Cyan for color representation and \
  6206. can effectively be used only for monochrome images."));
  6207.     GetTemplateBounds(StereoTemplate, "Green/Red", &l, &r, &b, &t);
  6208.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONLGLASSES, (char *) 0);
  6209.     SetVar(icon, BACKGROUND, NewInt(UIGREEN));
  6210.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6211.     PrefixList(panelContents, icon);
  6212.     SetVar(icon, PARENT, panelContents);
  6213.     icon = NewIcon(r - ICONSIZE / 2 - MAJORBORDER, (b + t) / 2, ICONRGLASSES, (char *) 0);
  6214.     SetVar(icon, BACKGROUND, NewInt(UIRED));
  6215.     SetMethod(icon, PRESS, (FuncTyp) 0);
  6216.     PrefixList(panelContents, icon);
  6217.     SetVar(icon, PARENT, panelContents);
  6218.  
  6219.     button = TemplateRadioButton(StereoTemplate, "Odd/Even");
  6220.     AddRadioButton(radio, button);
  6221.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6222. perspective images are shown, the image for the left eye on the odd scan lines \
  6223. and the image for the right eye on the even scan lines.  This may work in NTSC mode \
  6224. for some LCD flip glasses that switch during the vertical retrace and with some \
  6225. stereo LCD overhead projector screens.  Because of the \
  6226. way that the images must be interlaced, this will only work for images made up of polygons \
  6227. with no lines."));
  6228.  
  6229.     button = TemplateRadioButton(StereoTemplate, "Even/Odd");
  6230.     AddRadioButton(radio, button);
  6231.     SetVar(button, HELPSTRING, NewString("When this button is down, two \
  6232. perspective images are shown, the image for the left eye on the even scan lines \
  6233. and the image for the right eye on the odd scan lines.  This may work in NTSC mode \
  6234. for some LCD flip glasses that switch during the vertical retrace and with some \
  6235. stereo LCD overhead projector screens.  Because of the \
  6236. way that the images must be interlaced, this will only work for images made up of polygons \
  6237. with no lines."));
  6238.  
  6239.     button = TemplateRadioButton(StereoTemplate, "Crystal Eyes");
  6240.     AddRadioButton(radio, button);
  6241.     SetVar(button, HELPSTRING, NewString("When this button is down, stereo \
  6242. images will be displayed for use with the Crystal Eyes system.  In order for this \
  6243. to work, you must have a set of Crystal Eyes goggles.  Also, the visualization \
  6244. window must be set to full screen, and the control panel must be hidden.  Once you \
  6245. have set the visualization window to full screen, press the F11 key to toggle \
  6246. the Crystal Eyes goggles on and off."));
  6247.  
  6248.     if (!hasStereo)
  6249.     {
  6250.     ActivateButton(button, false);
  6251.     }
  6252.  
  6253.     button = TemplateRadioButton(StereoTemplate, "CAVE");
  6254.     AddRadioButton(radio, button);
  6255.     SetVar(button, HELPSTRING, NewString("When this button is down, stereo \
  6256. images will be generated for use with the CAVE virtual reality environment.  Or, rather \
  6257. they would be if this were implemented yet, but it's not.  However, we expect to \
  6258. show this at SIGGRAPH '94."));
  6259.     ActivateButton(button, false);
  6260.  
  6261.     var = GetIntVar("AddStereoControls", observer, STEREOMODE);
  6262.     if (!var)
  6263.     {
  6264.     SetVar(observer, STEREOMODE, NewInt(SM_MONO));
  6265.     }
  6266.     AssocDirectControlWithVar(radio, observer, STEREOMODE);
  6267.  
  6268.     /*Binocular spacing*/
  6269.     textBox = TemplateTextBox(StereoTemplate, "Binocular Legend", PLAIN, "Interocular Spacing:");
  6270.     SetVar(textBox, PARENT, panelContents);
  6271.     PrefixList(panelContents, textBox);
  6272.  
  6273.     var = GetRealVar("AddViewControls", observer, BINOCULARITY);
  6274.     if (!var)
  6275.     {
  6276.     SetVar(observer, BINOCULARITY, NewReal(0.4));
  6277.     }
  6278.     textBox = TemplateTextBox(StereoTemplate, "Binocular Spacing", EDITABLE + WITH_PIT + ONE_LINE, "");
  6279.     SetVar(textBox, PARENT, panelContents);
  6280.     PrefixList(panelContents, textBox);
  6281.     SetVar(textBox, HELPSTRING, NewString("This editable text box controls \
  6282. the spacing between the eyes while viewing a visualization in one of the stereo \
  6283. modes.  The larger the number, the stronger the stereo effect will be.  The \
  6284. maximum comfortable stereo effect may vary depending on the particular image, \
  6285. so you may have to experiment to get the best effect.  If you have a dial box, holding \
  6286. down the Alt key while turning the lower right dial will change this spacing."));
  6287.     SetTextAlign(textBox, RIGHTALIGN);
  6288.     AssocTextRealControlWithVar(textBox, observer, BINOCULARITY, 0.0, plusInf, 0);
  6289.  
  6290.     return ObjTrue;
  6291. }
  6292.  
  6293. static ObjPtr AddPlacementControls(observer, panelContents)
  6294. ObjPtr observer, panelContents;
  6295. /*Adds placement controls to a panelContents*/
  6296. {
  6297.     ObjPtr checkBox, textBox, titleBox, radio, control;
  6298.     ObjPtr var;
  6299.  
  6300.     /*Put in the controls on the right*/
  6301.     textBox = TemplateTextBox(PlacementTemplate, "X", PLAIN, "X");
  6302.     PrefixList(panelContents, textBox);
  6303.     SetVar(textBox, PARENT, panelContents);
  6304.     SetTextAlign(textBox, CENTERALIGN);
  6305.     textBox = TemplateTextBox(PlacementTemplate, "Y", PLAIN, "Y");
  6306.     PrefixList(panelContents, textBox);
  6307.     SetVar(textBox, PARENT, panelContents);
  6308.     SetTextAlign(textBox, CENTERALIGN);
  6309.     textBox = TemplateTextBox(PlacementTemplate, "Z", PLAIN, "Z");
  6310.     PrefixList(panelContents, textBox);
  6311.     SetVar(textBox, PARENT, panelContents);
  6312.     SetTextAlign(textBox, CENTERALIGN);
  6313.  
  6314.     textBox = TemplateTextBox(PlacementTemplate, "Position Text", PLAIN, "Position:");
  6315.     PrefixList(panelContents, textBox);
  6316.     SetVar(textBox, PARENT, panelContents);
  6317.     SetTextAlign(textBox, LEFTALIGN);
  6318.  
  6319.     textBox = TemplateTextBox(PlacementTemplate, "XPosn", EDITABLE + WITH_PIT + ONE_LINE, "");
  6320.     PrefixList(panelContents, textBox);
  6321.     SetVar(textBox, PARENT, panelContents);
  6322.     SetVar(textBox, REPOBJ, observer);
  6323.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6324.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, USESPACECOORDS);
  6325.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, SPACESCALE);
  6326.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, SPACEORIGIN);
  6327.     SetVar(textBox, WHICHINDEX, NewInt(0));
  6328.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverLocation);
  6329.     SetMethod(textBox, APPEARANCE, MakeObserverLocationAppearance);
  6330.     SetTextAlign(textBox, RIGHTALIGN);
  6331.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6332. X coordinate of the observer's position.  Enter a new position and press the Enter \
  6333. key to change it.\n"));
  6334.  
  6335.     textBox = TemplateTextBox(PlacementTemplate, "YPosn", EDITABLE + WITH_PIT + ONE_LINE, "");
  6336.     PrefixList(panelContents, textBox);
  6337.     SetVar(textBox, PARENT, panelContents);
  6338.     SetVar(textBox, REPOBJ, observer);
  6339.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6340.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, USESPACECOORDS);
  6341.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, SPACESCALE);
  6342.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, SPACEORIGIN);
  6343.     SetVar(textBox, WHICHINDEX, NewInt(1));
  6344.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverLocation);
  6345.     SetMethod(textBox, APPEARANCE, MakeObserverLocationAppearance);
  6346.     SetTextAlign(textBox, RIGHTALIGN);
  6347.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6348. Y coordinate of the observer's position.  Enter a new position and press the Enter \
  6349. key to change it.\n"));
  6350.  
  6351.     textBox = TemplateTextBox(PlacementTemplate, "ZPosn", EDITABLE + WITH_PIT + ONE_LINE, "");
  6352.     PrefixList(panelContents, textBox);
  6353.     SetVar(textBox, PARENT, panelContents);
  6354.     SetVar(textBox, REPOBJ, observer);
  6355.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6356.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, USESPACECOORDS);
  6357.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, SPACESCALE);
  6358.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, SPACEORIGIN);
  6359.     SetVar(textBox, WHICHINDEX, NewInt(2));
  6360.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverLocation);
  6361.     SetMethod(textBox, APPEARANCE, MakeObserverLocationAppearance);
  6362.     SetTextAlign(textBox, RIGHTALIGN);
  6363.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6364. Z coordinate of the observer's position.  Enter a new position and press the Enter \
  6365. key to change it.\n"));
  6366.  
  6367.     textBox = TemplateTextBox(PlacementTemplate, "Look At Text", PLAIN, "Look At:");
  6368.     PrefixList(panelContents, textBox);
  6369.     SetVar(textBox, PARENT, panelContents);
  6370.     SetTextAlign(textBox, LEFTALIGN);
  6371.  
  6372.     textBox = TemplateTextBox(PlacementTemplate, "XLookat", EDITABLE + WITH_PIT + ONE_LINE, "");
  6373.     PrefixList(panelContents, textBox);
  6374.     SetVar(textBox, PARENT, panelContents);
  6375.     SetVar(textBox, REPOBJ, observer);
  6376.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6377.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6378.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6379.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, USESPACECOORDS);
  6380.     SetVar(textBox, WHICHINDEX, NewInt(0));
  6381.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverLookat);
  6382.     SetMethod(textBox, APPEARANCE, MakeObserverLookatAppearance);
  6383.     SetTextAlign(textBox, RIGHTALIGN);
  6384.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6385. X coordinate of the point at which the observer is looking.  Enter a new position and press the Enter \
  6386. key to change it.\n"));
  6387.  
  6388.     textBox = TemplateTextBox(PlacementTemplate, "YLookat", EDITABLE + WITH_PIT + ONE_LINE, "");
  6389.     PrefixList(panelContents, textBox);
  6390.     SetVar(textBox, PARENT, panelContents);
  6391.     SetVar(textBox, REPOBJ, observer);
  6392.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6393.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6394.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6395.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, USESPACECOORDS);
  6396.     SetVar(textBox, WHICHINDEX, NewInt(1));
  6397.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverLookat);
  6398.     SetMethod(textBox, APPEARANCE, MakeObserverLookatAppearance);
  6399.     SetTextAlign(textBox, RIGHTALIGN);
  6400.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6401. Y coordinate of the point at which the observer is looking.  Enter a new position and press the Enter \
  6402. key to change it.\n"));
  6403.  
  6404.     textBox = TemplateTextBox(PlacementTemplate, "ZLookat", EDITABLE + WITH_PIT + ONE_LINE, "");
  6405.     PrefixList(panelContents, textBox);
  6406.     SetVar(textBox, PARENT, panelContents);
  6407.     SetVar(textBox, REPOBJ, observer);
  6408.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6409.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6410.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6411.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, USESPACECOORDS);
  6412.     SetVar(textBox, WHICHINDEX, NewInt(2));
  6413.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverLookat);
  6414.     SetMethod(textBox, APPEARANCE, MakeObserverLookatAppearance);
  6415.     SetTextAlign(textBox, RIGHTALIGN);
  6416.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6417. Z coordinate of the point at which the observer is looking.  Enter a new position and press the Enter \
  6418. key to change it.\n"));
  6419.  
  6420.     textBox = TemplateTextBox(PlacementTemplate, "Up Text", PLAIN, "Up Vector:");
  6421.     PrefixList(panelContents, textBox);
  6422.     SetVar(textBox, PARENT, panelContents);
  6423.     SetTextAlign(textBox, LEFTALIGN);
  6424.  
  6425.     textBox = TemplateTextBox(PlacementTemplate, "XUp", EDITABLE + WITH_PIT + ONE_LINE, "");
  6426.     PrefixList(panelContents, textBox);
  6427.     SetVar(textBox, PARENT, panelContents);
  6428.     SetVar(textBox, REPOBJ, observer);
  6429.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6430.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6431.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6432.     SetVar(textBox, WHICHINDEX, NewInt(0));
  6433.     ActivateTextBox(textBox, false);
  6434.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverUp);
  6435.     SetMethod(textBox, APPEARANCE, MakeObserverUpAppearance);
  6436.     SetTextAlign(textBox, RIGHTALIGN);
  6437.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6438. X coordinate of the up vector.  Enter a new coordinate and press the Enter \
  6439. key to change it.\n"));
  6440.  
  6441.     textBox = TemplateTextBox(PlacementTemplate, "YUp", EDITABLE + WITH_PIT + ONE_LINE, "");
  6442.     PrefixList(panelContents, textBox);
  6443.     SetVar(textBox, PARENT, panelContents);
  6444.     SetVar(textBox, REPOBJ, observer);
  6445.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6446.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6447.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6448.     SetVar(textBox, WHICHINDEX, NewInt(1));
  6449.     ActivateTextBox(textBox, false);
  6450.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverUp);
  6451.     SetMethod(textBox, APPEARANCE, MakeObserverUpAppearance);
  6452.     SetTextAlign(textBox, RIGHTALIGN);
  6453.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6454. Y coordinate of the up vector.  Enter a new coordinate and press the Enter \
  6455. key to change it.\n"));
  6456.  
  6457.     textBox = TemplateTextBox(PlacementTemplate, "ZUp", EDITABLE + WITH_PIT + ONE_LINE, "");
  6458.     PrefixList(panelContents, textBox);
  6459.     SetVar(textBox, PARENT, panelContents);
  6460.     SetVar(textBox, REPOBJ, observer);
  6461.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6462.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6463.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6464.     SetVar(textBox, WHICHINDEX, NewInt(2));
  6465.     ActivateTextBox(textBox, false);
  6466.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverUp);
  6467.     SetMethod(textBox, APPEARANCE, MakeObserverUpAppearance);
  6468.     SetTextAlign(textBox, RIGHTALIGN);
  6469.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6470. Z coordinate of the up vector.  Enter a new coordinate and press the Enter \
  6471. key to change it.\n"));
  6472.  
  6473.     textBox = TemplateTextBox(PlacementTemplate, "Quaternion Text", PLAIN, "Rotation:");
  6474.     PrefixList(panelContents, textBox);
  6475.     SetVar(textBox, PARENT, panelContents);
  6476.     SetTextAlign(textBox, LEFTALIGN);
  6477.  
  6478.     textBox = TemplateTextBox(PlacementTemplate, "QScalar", EDITABLE + WITH_PIT + ONE_LINE, "");
  6479.     PrefixList(panelContents, textBox);
  6480.     SetVar(textBox, PARENT, panelContents);
  6481.     SetVar(textBox, REPOBJ, observer);
  6482.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6483.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6484.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6485.     SetVar(textBox, WHICHINDEX, NewInt(0));
  6486.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverQuaternion);
  6487.     SetMethod(textBox, APPEARANCE, MakeObserverQuaternionAppearance);
  6488.     SetTextAlign(textBox, RIGHTALIGN);
  6489.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6490. scalar part of the quaternion used for rotation.  This is cos(theta/2), where \
  6491. theta is the amount of rotation.  Enter a new value and press the Enter \
  6492. key to change it.\n"));
  6493.  
  6494.     textBox = TemplateTextBox(PlacementTemplate, "QX", EDITABLE + WITH_PIT + ONE_LINE, "");
  6495.     PrefixList(panelContents, textBox);
  6496.     SetVar(textBox, PARENT, panelContents);
  6497.     SetVar(textBox, REPOBJ, observer);
  6498.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6499.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6500.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6501.     SetVar(textBox, WHICHINDEX, NewInt(1));
  6502.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverQuaternion);
  6503.     SetMethod(textBox, APPEARANCE, MakeObserverQuaternionAppearance);
  6504.     SetTextAlign(textBox, RIGHTALIGN);
  6505.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6506. X coordinate of the vector part of the quaternion used for rotation.  The \
  6507. vector points along the rotation axis with magnitude sin(theta/2), where theta \
  6508. is the amount of rotation.  Enter a new coordinate and press the Enter \
  6509. key to change it.\n"));
  6510.  
  6511.     textBox = TemplateTextBox(PlacementTemplate, "QY", EDITABLE + WITH_PIT + ONE_LINE, "");
  6512.     PrefixList(panelContents, textBox);
  6513.     SetVar(textBox, PARENT, panelContents);
  6514.     SetVar(textBox, REPOBJ, observer);
  6515.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6516.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6517.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6518.     SetVar(textBox, WHICHINDEX, NewInt(2));
  6519.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverQuaternion);
  6520.     SetMethod(textBox, APPEARANCE, MakeObserverQuaternionAppearance);
  6521.     SetTextAlign(textBox, RIGHTALIGN);
  6522.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6523. Y coordinate of the vector part of the quaternion used for rotation.  The \
  6524. vector points along the rotation axis with magnitude sin(theta/2), where theta \
  6525. is the amount of rotation.  Enter a new coordinate and press the Enter \
  6526. key to change it.\n"));
  6527.  
  6528.     textBox = TemplateTextBox(PlacementTemplate, "QZ", EDITABLE + WITH_PIT + ONE_LINE, "");
  6529.     PrefixList(panelContents, textBox);
  6530.     SetVar(textBox, PARENT, panelContents);
  6531.     SetVar(textBox, REPOBJ, observer);
  6532.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, LOCATION);
  6533.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FORWARDVECTOR);
  6534.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, FOCUSDIST);
  6535.     SetVar(textBox, WHICHINDEX, NewInt(3));
  6536.     SetMethod(textBox, CHANGEDVALUE, ChangeObserverQuaternion);
  6537.     SetMethod(textBox, APPEARANCE, MakeObserverQuaternionAppearance);
  6538.     SetTextAlign(textBox, RIGHTALIGN);
  6539.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6540. Z coordinate of the vector part of the quaternion used for rotation.  The \
  6541. vector points along the rotation axis with magnitude sin(theta/2), where theta \
  6542. is the amount of rotation.  Enter a new coordinate and press the Enter \
  6543. key to change it.\n"));
  6544.  
  6545.     textBox = TemplateTextBox(PlacementTemplate, "Origin Text", PLAIN, "Origin:");
  6546.     PrefixList(panelContents, textBox);
  6547.     SetVar(textBox, PARENT, panelContents);
  6548.     SetTextAlign(textBox, LEFTALIGN);
  6549.  
  6550.     textBox = TemplateTextBox(PlacementTemplate, "XOrigin", EDITABLE + WITH_PIT + ONE_LINE, "");
  6551.     PrefixList(panelContents, textBox);
  6552.     SetVar(textBox, PARENT, panelContents);
  6553.     SetTextAlign(textBox, RIGHTALIGN);
  6554.     SetMethod(textBox, EXTRAAPPEARANCE, HideOnAutoAdjust);
  6555.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, AUTOADJUST);
  6556.     AssocIndexedTextRealControlWithVar(textBox,
  6557.     observer, SPACEORIGIN, 0, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
  6558.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6559. X coordinate of the observer's origin in space coordinates.  By default, \
  6560. this is the center of all visualization objects in the space.  If the Adjust to Space \
  6561. check box is on, the origin will automatically be adjusted according to the \
  6562. objects within the space.  If the check box is off, you can change the origin \
  6563. by entering a number in the text box and pressing the Enter key."));
  6564.  
  6565.     textBox = TemplateTextBox(PlacementTemplate, "YOrigin", EDITABLE + WITH_PIT + ONE_LINE, "");
  6566.     PrefixList(panelContents, textBox);
  6567.     SetVar(textBox, PARENT, panelContents);
  6568.     SetTextAlign(textBox, RIGHTALIGN);
  6569.     SetMethod(textBox, EXTRAAPPEARANCE, HideOnAutoAdjust);
  6570.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, AUTOADJUST);
  6571.     AssocIndexedTextRealControlWithVar(textBox,
  6572.     observer, SPACEORIGIN, 1, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
  6573.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6574. Y coordinate of the observer's origin in space coordinates.  By default, \
  6575. this is the center of all visualization objects in the space.  If the Adjust to Space \
  6576. check box is on, the origin will automatically be adjusted according to the \
  6577. objects within the space.  If the check box is off, you can change the origin \
  6578. by entering a number in the text box and pressing the Enter key."));
  6579.  
  6580.     textBox = TemplateTextBox(PlacementTemplate, "ZOrigin", EDITABLE + WITH_PIT + ONE_LINE, "");
  6581.     PrefixList(panelContents, textBox);
  6582.     SetVar(textBox, PARENT, panelContents);
  6583.     SetTextAlign(textBox, RIGHTALIGN);
  6584.     SetMethod(textBox, EXTRAAPPEARANCE, HideOnAutoAdjust);
  6585.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, AUTOADJUST);
  6586.     AssocIndexedTextRealControlWithVar(textBox,
  6587.     observer, SPACEORIGIN, 2, minusInf, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
  6588.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6589. Y coordinate of the observer's origin in space coordinates.  By default, \
  6590. this is the center of all visualization objects in the space.  If the Adjust to Space \
  6591. check box is on, the origin will automatically be adjusted according to the \
  6592. objects within the space.  If the check box is off, you can change the origin \
  6593. by entering a number in the text box and pressing the Enter key."));
  6594.  
  6595.     /*Scaling*/
  6596.     textBox = TemplateTextBox(PlacementTemplate, "Scaling Text", PLAIN, "Scaling:");
  6597.     PrefixList(panelContents, textBox);
  6598.     SetVar(textBox, PARENT, panelContents);
  6599.     SetTextAlign(textBox, LEFTALIGN);
  6600.  
  6601.     textBox = TemplateTextBox(PlacementTemplate, "Scaling", EDITABLE + WITH_PIT + ONE_LINE, "");
  6602.     PrefixList(panelContents, textBox);
  6603.     SetVar(textBox, PARENT, panelContents);
  6604.     SetTextAlign(textBox, RIGHTALIGN);
  6605.     SetMethod(textBox, EXTRAAPPEARANCE, HideOnAutoAdjust);
  6606.     DeclareIndirectDependency(textBox, APPEARANCE, REPOBJ, AUTOADJUST);
  6607.     AssocTextRealControlWithVar(textBox,
  6608.     observer, SPACESCALE, 0.0, plusInf, TR_NE_BOTTOM | TR_NE_TOP);
  6609.     SetVar(textBox, HELPSTRING, NewString("This text box controls the \
  6610. scaling of the observer's coordinate in space coordinates.  This is the amount \
  6611. in space coordinates that corresponds to 1 unit in observer coordinates.  By default, \
  6612. the scaling is set so that all the objects in the space fit roughly within a \
  6613. sphere of radius 1 centered on the origin.  If the Adjust to Space \
  6614. check box is on, the scaling will automatically be adjusted according to the \
  6615. objects within the space.  If the check box is off, you can change the scaling \
  6616. by entering a number in the text box and pressing the Enter key."));
  6617.  
  6618.     /*Adjust to space*/
  6619.     checkBox = TemplateCheckBox(PlacementTemplate, "Adjust to Space", false);
  6620.     PrefixList(panelContents, checkBox);
  6621.     SetVar(checkBox, PARENT, panelContents);
  6622.     AssocDirectControlWithVar(checkBox, observer, AUTOADJUST);
  6623.     SetVar(checkBox, HELPSTRING, NewString("This check box controls \
  6624. whether the observer's origin and scaling are automatically adjusted to the space being \
  6625. drawn.  When this box is off, you can enter the origin and scaling manually.  \
  6626. When it is on, the origin and scaling will change depending on which space is \
  6627. being drawn.  If the observer controls several spaces with different coordinate \
  6628. systems, this may result in the \
  6629. numbers changing rapidly as the individual spaces are drawn."));
  6630.  
  6631.     /*Coordinate systems*/
  6632.     radio = NewRadioButtonGroup("Coordinate System");
  6633.     PrefixList(panelContents, radio);
  6634.     SetVar(radio, PARENT, panelContents);
  6635.     SetVar(checkBox, HELPSTRING, NewString("This radio button group \
  6636. controls whether coordinates are displayed in observer coordinates or space \
  6637. coordinates.\n\nObserver coordinates provide a normalized \
  6638. coordinate system for viewing, where the field of view is approximately covered \
  6639. by a unit sphere centered at (0, 0, 0).  Observer coordinates are dimensionless \
  6640. and are always the same, no matter how many spaces are controlled by the observer.\n\n\
  6641. Space coordinates are the coordinates \
  6642. used by the visualization objects within the space.  These may be kilometers, angstroms, \
  6643. or whatever is appropriate.  Space coordinates can be more convenient if you \
  6644. want to go to a certain point within the space.  If an observer controls more \
  6645. than one space, the space coordinates may be different.  We recommend that you \
  6646. only use space coordinates when controlling a single space."));
  6647.  
  6648.     checkBox = TemplateRadioButton(PlacementTemplate, "Observer Coordinates");
  6649.     AddRadioButton(radio, checkBox);
  6650.  
  6651.     checkBox = TemplateRadioButton(PlacementTemplate, "Space Coordinates");
  6652.     AddRadioButton(radio, checkBox);
  6653.  
  6654.     AssocDirectControlWithVar(radio, observer, USESPACECOORDS);
  6655.  
  6656.     return ObjTrue;
  6657. }
  6658.  
  6659. static ObjPtr ShowObserverControls(object, windowName)
  6660. ObjPtr object;
  6661. char *windowName;
  6662. /*Makes a new control window to control observer object*/
  6663. {
  6664.     WinInfoPtr controlWindow;
  6665.     ObjPtr var;
  6666.     ObjPtr panel;
  6667.     ObjPtr controlField;
  6668.     ObjPtr contents;
  6669.     ObjPtr curObj;
  6670.     ObjPtr firstButton = NULLOBJ;
  6671.     int left, right, bottom, top, width;
  6672.     WinInfoPtr dialogExists;
  6673.     Bool abortp = false;
  6674.     Bool saveSettings = false;
  6675.  
  6676.     dialogExists = DialogExists((WinInfoPtr) object, NewString("Controls"));
  6677.     controlWindow = GetDialog((WinInfoPtr) object, NewString("Controls"), windowName, 
  6678.     SMCWINWIDTH, SMCWINHEIGHT, SMCWINWIDTH, SMCWINHEIGHT, WINFIXEDSIZE + WINUI);
  6679.     
  6680.     if (!dialogExists)
  6681.     {
  6682.     SetVar((ObjPtr) controlWindow, REPOBJ, object);
  6683.  
  6684.     /*Add in help string*/
  6685.     SetVar((ObjPtr) controlWindow, HELPSTRING,
  6686.         NewString("This window shows controls for an observer.  The observer \
  6687. represents you, looking into the space containing visualization objects.  The controls \
  6688. allow you to change your viewing angle, location, orientation, and clipping planes.\n\n\
  6689. At the right is an icon corral showing a series of icons.  Each icon represents a \
  6690. set of attributes of the observer.  On the left are the controls for \
  6691. the selected set of attributes.  \
  6692. Use Help In Context and click on the various controls to find out what they do.  \
  6693. Click on a different icon to choose a different set of attributes."));
  6694.  
  6695.     /*Add in a panel*/
  6696.     panel = NewPanel(greyPanelClass, 0, SMCWINWIDTH, 0, SMCWINHEIGHT);
  6697.     if (!panel)
  6698.     {
  6699.         return ObjFalse;
  6700.     }
  6701.     contents = GetVar((ObjPtr) controlWindow, CONTENTS);
  6702.     PrefixList(contents, panel);
  6703.     SetVar(panel, PARENT, (ObjPtr) controlWindow);
  6704.  
  6705.     contents = GetVar(panel, CONTENTS);
  6706.  
  6707.     /*Add in a control field*/
  6708.     controlField = NewControlField(SMCWINWIDTH - CORRALBORDER - SMCWINCORRALWIDTH,
  6709.                    SMCWINWIDTH - CORRALBORDER,
  6710.                    CORRALBORDER,
  6711.                    SMCWINHEIGHT - CORRALBORDER,
  6712.                 "Observer Attributes", OBJECTSFROMTOP | BARRIGHT);
  6713.     SetVar(controlField, HELPSTRING,
  6714.        NewString("This icon button group shows sets of attributes of the observer \
  6715. object that can be modified.  The left side of the panel shows controls for the \
  6716. attribute given by the selected icon button.  To show another set of \
  6717. attributes, press another button."));
  6718.     SetVar(controlField, PARENT, panel);
  6719.     PrefixList(contents, controlField);
  6720.  
  6721.     contents = GetVar(controlField, CONTENTS);
  6722.  
  6723.     /*Fill the control field up with buttons*/
  6724.     curObj = object;
  6725.     top = -MAJORBORDER;
  6726.     width = SMCWINCCWIDTH;
  6727.  
  6728.     saveSettings = GetPredicate(curObj, CANSAVESETTINGS);
  6729.  
  6730.     while (curObj)
  6731.     {
  6732.         ObjPtr icon;
  6733.         icon = Get1Var(curObj, CONTROLICON);
  6734.         if (icon)
  6735.         {
  6736.         ObjPtr button;
  6737.         ObjPtr panelContents;
  6738.         FuncTyp method;
  6739.         int whichIcon;
  6740.         char *name;
  6741.  
  6742.         if (firstButton && abortp) break;
  6743.  
  6744.         var = GetIntVar("ShowObserverControls", icon, WHICHICON);
  6745.         if (var)
  6746.         {
  6747.             whichIcon = GetInt(var);
  6748.         }
  6749.         else
  6750.         {
  6751.             whichIcon = ICONQUESTION;
  6752.         }
  6753.  
  6754.         var = GetStringVar("ShowObserverControls", icon, NAME);
  6755.         if (var)
  6756.         {
  6757.             name = GetString(var);
  6758.         }
  6759.         else
  6760.         {
  6761.             name = "Unknown";
  6762.         }
  6763.  
  6764.         button = NewIconLabeledButton(0, width, top - SMCWINICONBUTHEIGHT, top,
  6765.             whichIcon, UIYELLOW, name, BS_PITTED);
  6766.         SetMethod(button, ICONEXTRADRAW, GetMethod(icon, ICONEXTRADRAW));
  6767.         SetVar(button, REPOBJ, object);
  6768.         SetMethod(button, CHANGEDVALUE, ChangeControlPanelButton);
  6769.  
  6770.         SetVar(button, PANEL, panel);
  6771.  
  6772.         if (!firstButton)
  6773.         {
  6774.             firstButton = button;
  6775.         }
  6776.  
  6777.         if (saveSettings)
  6778.         {
  6779.             SetVar(button, CANSAVESETTINGS, ObjTrue);
  6780.         }
  6781.         /*Make a new panel contents just for this button*/
  6782.         panelContents = NewList();
  6783.         PrefixList(panelContents, controlField);
  6784.         SetVar(panelContents, PARENT, panel);
  6785.         SetVar(button, PANELCONTENTS, panelContents);
  6786.         SetVar(button, PARENT, panelContents);
  6787.  
  6788.         /*Give the button a chance to add controls*/
  6789.         method = Get1Method(curObj, ADDCONTROLS);
  6790.         if (method)
  6791.         {
  6792.             SetVar(button, CONTROLSADDED, ObjFalse);
  6793.             SetMethod(button, ADDCONTROLS, method);
  6794.         }
  6795.         else
  6796.         {
  6797.             SetVar(button, CONTROLSADDED, ObjTrue);
  6798.         }
  6799.         PrefixList(contents, button);
  6800.         SetVar(button, PARENT, controlField);
  6801.         top -= SMCWINICONBUTHEIGHT + MINORBORDER;
  6802.     
  6803.         }
  6804.         if (GetPredicate(curObj, ABORTCONTROLS)) abortp = true;
  6805.         curObj = ClassOf(curObj);
  6806.     }
  6807.  
  6808.     /*Adjust the scroll bars*/
  6809.     RecalcScroll(controlField);
  6810.  
  6811.     if (firstButton)
  6812.     {
  6813.         SetValue(firstButton, NewInt(1));
  6814.         SetVar(controlField, BUTTON, firstButton);
  6815.     }
  6816.     }
  6817.  
  6818.     return (ObjPtr) controlWindow;
  6819. }
  6820.  
  6821. ObjPtr ShowRendererControls(renderer, windowName)
  6822. ObjPtr renderer;
  6823. ObjPtr windowName;
  6824. /*Makes a new renderer window to control renderer.*/
  6825. {
  6826.     WinInfoPtr rendererWindow;
  6827.     ObjPtr name;
  6828.     ObjPtr var;
  6829.     ObjPtr panel;
  6830.     ObjPtr contents;
  6831.     WinInfoPtr dialogExists;
  6832.     ObjPtr whichDialog;
  6833.     int left, right, bottom, top;
  6834.  
  6835.     if (!renderer) return NULLOBJ;
  6836.  
  6837.     name = GetVar(renderer, NAME);
  6838.  
  6839.     whichDialog = NewString("Renderer");
  6840.     dialogExists = DialogExists((WinInfoPtr) renderer, whichDialog);
  6841.     if (name)
  6842.     {
  6843.     strncpy(tempStr, GetString(name), TEMPSTRSIZE);
  6844.     tempStr[TEMPSTRSIZE] = 0;
  6845.     }
  6846.     else
  6847.     {
  6848.     strcpy(tempStr, "Renderer");
  6849.     }
  6850.     GetTemplateBounds(RendererTemplate, "Panel", &left, &right, &bottom, &top);
  6851.  
  6852.     rendererWindow = GetDialog((WinInfoPtr) renderer, whichDialog, tempStr, 
  6853.     right - left, top - bottom, right - left, top - bottom, WINUI);
  6854.  
  6855.     if (!dialogExists)
  6856.     {
  6857.     SetVar((ObjPtr) rendererWindow, REPOBJ, renderer);
  6858.  
  6859.     /*Add controls*/
  6860.     SetVar((ObjPtr) rendererWindow, HELPSTRING,
  6861.         NewString("This window shows controls that affect the rendering of \
  6862. the objects within the space."));
  6863.     contents = GetVar((ObjPtr) rendererWindow, CONTENTS);
  6864.  
  6865.     /*Add in a panel*/
  6866.     panel = TemplatePanel(RendererTemplate, "Panel");
  6867.     if (!panel)
  6868.     {
  6869.         return ObjFalse;
  6870.     }
  6871.     PrefixList(contents, panel);
  6872.     SetVar(panel, PARENT, (ObjPtr) rendererWindow);
  6873.  
  6874.     contents = GetListVar("ShowRendererControls", panel, CONTENTS);
  6875.     if (contents)
  6876.     {
  6877.         ObjPtr titleBox;
  6878.         ObjPtr button;
  6879.         ObjPtr radio;
  6880.         ObjPtr var;
  6881.  
  6882.         /*Make the title box around render type*/
  6883.         titleBox = TemplateTitleBox(RendererTemplate, "Renderer Type");
  6884.         PrefixList(contents, titleBox);
  6885.         SetVar(titleBox, PARENT, panel);
  6886.  
  6887.         /*Make the no renderer button*/
  6888.         radio = NewRadioButtonGroup("Renderer");
  6889.         SetVar(radio, HELPSTRING, NewString("These radio buttons control what kind of renderer is \
  6890. used to render the objects within the space.  At present, there is only one renderer available: a hardware renderer."));
  6891.  
  6892.         button = TemplateRadioButton(RendererTemplate, "No Renderer");
  6893.         AddRadioButton(radio, button);
  6894.         SetVar(button, HELPSTRING,
  6895.         NewString("This button causes rendering not to be done on the space.  \
  6896. This is sometimes useful as to hide all the visualization objects."));
  6897.  
  6898.         /*Make the full color hardware renderer button*/
  6899.         button = TemplateRadioButton(RendererTemplate,  "Full Color Hardware");
  6900.         AddRadioButton(radio, button);
  6901.         SetVar(button, HELPSTRING,
  6902.         NewString("This button sets the space to use the hardware renderer \
  6903. in full color mode.  In this mode, all the lighting and filtering is available.  Full color \
  6904. mode only works if the hardware has enough bitplanes to support it."));
  6905.         if (!hasRGB)
  6906.         {
  6907.         ActivateButton(button, false);
  6908.         }    
  6909.  
  6910.         /*Make the color map hardware renderer button*/
  6911.         button = TemplateRadioButton(RendererTemplate,  "Color Map Hardware");
  6912.         AddRadioButton(radio, button);
  6913.         SetVar(button, HELPSTRING,
  6914.         NewString("This button sets the space to use the hardware renderer \
  6915. in color map mode.  In this mode, lighting and image filtering is not available, but it can be useful \
  6916. on simple color shaded visualizations.  Color map mode only works on hardware with \
  6917. color maps."));
  6918.         if (!hasCmap)
  6919.         {
  6920.         ActivateButton(button, false);
  6921.         }    
  6922.  
  6923.         /*Set the radio button group*/
  6924.         PrefixList(contents, radio);
  6925.         SetVar(radio, PARENT, panel);
  6926.  
  6927.         var = GetVar(renderer, RENDERTYPE);
  6928.         if (!var)
  6929.         {
  6930.         SetVar(renderer, RENDERTYPE, NewInt(hasRGB ? RT_RGB_HARDWARE : RT_CMAP_HARDWARE));
  6931.         }
  6932.         AssocDirectControlWithVar(radio, renderer, RENDERTYPE);
  6933.  
  6934.         /*Make the title box around filter type*/
  6935.         titleBox = TemplateTitleBox(RendererTemplate, "Image Filter");
  6936.         PrefixList(contents, titleBox);
  6937.         SetVar(titleBox, PARENT, panel);
  6938.  
  6939.         /*Make the no filter button*/
  6940.         radio = NewRadioButtonGroup("Filter Type");
  6941.         PrefixList(contents, radio);
  6942.         SetVar(radio, PARENT, panel);
  6943.  
  6944.         SetVar(radio, HELPSTRING,
  6945.         NewString("These radio buttons select the kind of filtration that is done \
  6946. to the image of the space after it has been rendererd.  Filtration only works when the \
  6947. space is set to full color mode."));
  6948.         button = TemplateRadioButton(RendererTemplate, "No Filter");
  6949.         AddRadioButton(radio, button);
  6950.         SetVar(button, HELPSTRING,
  6951.         NewString("This button causes the image of the space to be shown \
  6952. without filtration.  This is the fastest and is recommended for interactive work."));
  6953.  
  6954.         /*Make the shrink filter button*/
  6955.         button = TemplateRadioButton(RendererTemplate, "2 to 1 shrink");
  6956.         SetVar(button, HELPSTRING,
  6957.         NewString("This button causes the image of the space to be shrunk \
  6958. 2 to 1 and averaged before being displayed.  This only works well when the window is \
  6959. in full color mode and the entire window is shown on the screen and is not covered by \
  6960. any other window.  It produces pretty good results with video.  Use the Double Video Screen \
  6961. item in the Window menu."));
  6962.         AddRadioButton(radio, button);
  6963.         if (!hasRGB)
  6964.         {
  6965.         ActivateButton(button, false);
  6966.         }    
  6967.  
  6968.         /*Make the shrink filter button*/
  6969.         button = TemplateRadioButton(RendererTemplate, "Four neighbor average");
  6970.         SetVar(button, HELPSTRING,
  6971.         NewString("This button causes each pixel of image of the space to be \
  6972. averaged with its four neighbors in the horizontal and vertical directions before being displayed.  \
  6973. This only works well when the window is in full color mode and is not covered by \
  6974. other windows.  It produces pretty good results with video, \
  6975. though not as good as the shrinking filter."));
  6976.         AddRadioButton(radio, button);
  6977.         if (!hasRGB)
  6978.         {
  6979.         ActivateButton(button, false);
  6980.         }    
  6981.  
  6982.         SetVar(radio, REPOBJ, renderer);
  6983.         var = GetIntVar("ShowRendererControls", renderer, FILTERTYPE);
  6984.         if (!var)
  6985.         {
  6986.         SetVar(renderer, FILTERTYPE, NewInt(FT_NONE));
  6987.         }
  6988.         AssocDirectControlWithVar(radio, renderer, FILTERTYPE);
  6989.     }
  6990.     }
  6991.  
  6992.     return (ObjPtr) rendererWindow;
  6993. }
  6994.  
  6995. ObjPtr MakeClockTimeFormat(clock)
  6996. ObjPtr clock;
  6997. /*Makes a clock's TIMEFORMAT variable*/
  6998. {
  6999.     ObjPtr datasets;
  7000.     int curFormat;
  7001.     ObjPtr format;
  7002.     ObjPtr element;
  7003.     long k;
  7004.  
  7005.     MakeVar(clock, DATASETS);
  7006.     curFormat = 0;
  7007.  
  7008.     datasets = GetVar(clock, DATASETS);
  7009.     if (datasets)
  7010.     {
  7011.     for (k = 0; k < DIMS(datasets)[0]; ++k)
  7012.     {
  7013.         element = GetObjectElement(datasets, &k);
  7014.         MakeVar(element, TIMEFORMAT);
  7015.         format = GetVar(element, TIMEFORMAT);
  7016.         if (format)
  7017.         {
  7018.         int newFormat;
  7019.         newFormat = GetInt(format);
  7020.         curFormat |= newFormat;
  7021.         }
  7022.     }
  7023.     }
  7024.     if (!curFormat)
  7025.     {
  7026.     curFormat = TF_SECONDS + TF_SUBSECONDS;
  7027.     }
  7028.     SetVar(clock, TIMEFORMAT, NewInt(curFormat));
  7029.     return ObjTrue;
  7030. }
  7031.  
  7032. ObjPtr MakeClockDatasets(clock)
  7033. ObjPtr clock;
  7034. /*Makes a clock's DATASETS variable, along with time slices*/
  7035. {
  7036.     ObjPtr list, spaces, array = NULLOBJ;
  7037.     ObjPtr element;
  7038.     long k;
  7039.     WinInfoPtr dialog;
  7040.  
  7041.     list = NewList();
  7042.     spaces = GetListVar("MakeClockDatasets", clock, SPACES);
  7043.     if (spaces)
  7044.     {
  7045.     ThingListPtr spaceRunner;
  7046.     spaceRunner = LISTOF(spaces);
  7047.     while (spaceRunner)
  7048.     {
  7049.         ObjPtr contents;
  7050.         contents = GetListVar("MakeClockDatasets", spaceRunner -> thing, CONTENTS);
  7051.         if (contents)
  7052.         {
  7053.         ThingListPtr contentsRunner;
  7054.         contentsRunner = LISTOF(contents);
  7055.         while (contentsRunner)
  7056.         {
  7057.             PrefixDatasets(list, contentsRunner -> thing);
  7058.             contentsRunner = contentsRunner -> next;
  7059.         }
  7060.         }
  7061.         spaceRunner = spaceRunner -> next;
  7062.     }
  7063.     }
  7064.  
  7065.     if (LISTOF(list))
  7066.     {
  7067.     /*There's something in the list*/
  7068.     Bool hasMinValue = false;
  7069.     real minValue;
  7070.  
  7071.     /*Convert to an array*/
  7072.     array = ListToArray(list);
  7073.  
  7074.     /*Remove duplicate elements*/
  7075.     array = Uniq(array);
  7076.  
  7077.     /*Sort the array*/
  7078.     array = SortArrayByStringVar(array, NAME);
  7079.  
  7080.     for (k = 0; k < DIMS(array)[0]; ++k)
  7081.     {
  7082.         ObjPtr name, timeSteps;
  7083.         element = GetObjectElement(array, &k);
  7084.  
  7085.         MakeVar(element, NAME);
  7086.         MakeVar(element, TIMESTEPS);
  7087.         name = GetVar(element, NAME);
  7088.         timeSteps = GetVar(element, TIMESTEPS);
  7089.         if (timeSteps)
  7090.         {
  7091.         if (hasMinValue)
  7092.         {
  7093.             minValue = MIN(minValue, *((real *)ELEMENTS(timeSteps)));
  7094.         }
  7095.         else
  7096.         {
  7097.             minValue = *((real *)ELEMENTS(timeSteps));
  7098.             hasMinValue = true;
  7099.         }
  7100.         }
  7101.     }
  7102.     if (hasMinValue)
  7103.     {
  7104.         if (!GetVar(clock, TIME))
  7105.         {
  7106.         SetClock(clock, minValue);
  7107.         }
  7108.     }
  7109.     else
  7110.     {
  7111.         SetVar(clock, TIME, NULLOBJ);
  7112.         ImInvalid(clock);
  7113.     }
  7114.     }
  7115.     else
  7116.     {
  7117.     SetVar(clock, TIME, NULLOBJ);
  7118.     ImInvalid(clock);
  7119.     }
  7120.  
  7121.     /*Set the clock's datasets to datasets*/
  7122.     SetVar(clock, DATASETS, array);
  7123.  
  7124.     return ObjTrue;
  7125. }
  7126.  
  7127. ObjPtr commonClock = NULLOBJ;
  7128. ObjPtr commonObserver = NULLOBJ;
  7129. ObjPtr commonRenderer = NULLOBJ;
  7130.  
  7131. int clockNum = 0;
  7132. int observerNum = 0;
  7133. int rendererNum = 0;
  7134.  
  7135. ObjPtr NewClock()
  7136. /*Returns a new clock*/
  7137. {
  7138.     ObjPtr retVal;
  7139.     ObjPtr name;
  7140.  
  7141.  
  7142.     if (oneClock)
  7143.     {
  7144.     if (commonClock) return commonClock;
  7145.     }
  7146.  
  7147.     retVal = NewObject(clockClass, 0);
  7148.     if (oneClock)
  7149.     {
  7150.     commonClock = retVal;
  7151.     }
  7152.     name = GetVar(clockClass, NAME);
  7153.     if (name)
  7154.     {
  7155.     sprintf(tempStr, "%s %d", GetString(name), ++clockNum);
  7156.     }
  7157.     else
  7158.     {
  7159.     sprintf(tempStr, "Clock %d", ++clockNum);
  7160.     }
  7161.     name = NewString(tempStr);
  7162.     SetVar(retVal, NAME, name);
  7163.     SetVar(retVal, SPACES, NewList());
  7164.     SetVar(retVal, CYCLECLOCK, ObjTrue);
  7165.     return retVal;
  7166. }
  7167.  
  7168. #ifdef PROTO
  7169. void GetUpVector(real upVector[3], ObjPtr observer)
  7170. #else
  7171. void GetUpVector(upVector, observer)
  7172. real upVector[3];
  7173. ObjPtr observer;
  7174. #endif
  7175. /*Gets the up vector of an observer*/
  7176. {
  7177.     ObjPtr var;
  7178.  
  7179.     MakeVar(observer, UPVECTOR);
  7180.     var = GetVar(observer, UPVECTOR);
  7181.     if (var)
  7182.     {
  7183.     Array2CArray(upVector, var);
  7184.     }
  7185.     else
  7186.     {
  7187.     upVector[0] = 0.0;
  7188.     upVector[1] = 1.0;
  7189.     upVector[2] = 0.0;
  7190.     }
  7191. }
  7192.  
  7193. #ifdef PROTO
  7194. void GetForwardVector(real forwardVector[3], ObjPtr observer)
  7195. #else
  7196. void GetForwardVector(forwardVector, observer)
  7197. real forwardVector[3];
  7198. ObjPtr observer;
  7199. #endif
  7200. /*Gets the forward vector of an observer*/
  7201. {
  7202.     ObjPtr var;
  7203.  
  7204.     MakeVar(observer, FORWARDVECTOR);
  7205.     var = GetVar(observer, FORWARDVECTOR);
  7206.     if (var)
  7207.     {
  7208.     Array2CArray(forwardVector, var);
  7209.     }
  7210.     else
  7211.     {
  7212.     forwardVector[0] = 0.0;
  7213.     forwardVector[1] = 0.0;
  7214.     forwardVector[2] = -1.0;
  7215.     }
  7216. }
  7217.  
  7218. static ObjPtr MakeObserverForwardVector(observer)
  7219. ObjPtr observer;
  7220. /*Makes the observer's forward vector*/
  7221. {
  7222.     real forwardVector[3];
  7223.     Quaternion rotQuat;
  7224.     ObjPtr var;
  7225.  
  7226.     MakeVar(observer, ROTQUAT);
  7227.     if (var = GetVar(observer, ROTQUAT))
  7228.     {
  7229.     /*There's a rotation quaternion, derive forward from that*/
  7230.     real temp[3];
  7231.  
  7232.     temp[0] = 0.0;
  7233.     temp[1] = 0.0;
  7234.     temp[2] = -1.0;
  7235.  
  7236.     Array2CArray(rotQuat, var);
  7237.     QRot(temp, rotQuat, forwardVector);
  7238.     }
  7239.     else
  7240.     {
  7241.     forwardVector[0] = 0.0;
  7242.     forwardVector[1] = 0.0;
  7243.     forwardVector[2] = -1.0;
  7244.     }
  7245.  
  7246.     var = NewRealArray(1, 3L);
  7247.     CArray2Array(var, forwardVector);
  7248.     SetVar(observer, FORWARDVECTOR, var);
  7249.     return ObjTrue;
  7250. }
  7251.  
  7252. static ObjPtr MakeObserverUpVector(observer)
  7253. ObjPtr observer;
  7254. /*Makes the observer's up vector*/
  7255. {
  7256.     real upVector[3];
  7257.     Quaternion rotQuat;
  7258.     ObjPtr var;
  7259.  
  7260.     MakeVar(observer, ROTQUAT);
  7261.     if (var = GetVar(observer, ROTQUAT))
  7262.     {
  7263.     /*There's a rotation quaternion, derive forward from that*/
  7264.     real temp[3];
  7265.  
  7266.     temp[0] = 0.0;
  7267.     temp[1] = 1.0;
  7268.     temp[2] = 0.0;
  7269.  
  7270.     Array2CArray(rotQuat, var);
  7271.     QRot(temp, rotQuat, upVector);
  7272.     }
  7273.     else
  7274.     {
  7275.     upVector[0] = 0.0;
  7276.     upVector[1] = 1.0;
  7277.     upVector[2] = 0.0;
  7278.     }
  7279.     var = NewRealArray(1, 3L);
  7280.     CArray2Array(var, upVector);
  7281.     SetVar(observer, UPVECTOR, var);
  7282.     return ObjTrue;
  7283. }
  7284.  
  7285. #ifdef PROTO
  7286. void GetAdjustedVectors(real forwardVector[3], real upVector[3], ObjPtr observer)
  7287. #else
  7288. void GetAdjustedVectors(forwardVector, upVector, observer)
  7289. real forwardVector[3];
  7290. real upVector[3];
  7291. ObjPtr observer;
  7292. #endif
  7293. /*Gets the vectors of an observer, adjusted for roll and pitch*/
  7294. {
  7295.     ObjPtr var;
  7296.     real temp[3];
  7297.     real sp, cp, t;
  7298.     Matrix rotDelta;
  7299.     real phi;
  7300.  
  7301.     GetUpVector(upVector, observer);
  7302.     GetForwardVector(forwardVector, observer);
  7303.  
  7304.     var = GetVar(observer, PITCH);
  7305.     if (var)
  7306.     {
  7307.     phi = GetReal(var);
  7308.     if (phi != 0.0)
  7309.     {
  7310.         /*Rotate down around pitch*/
  7311.         real side[3];
  7312.  
  7313.         sp = rsin(phi);
  7314.         cp = rcos(phi);
  7315.         t = 1.0 - cp;
  7316.     
  7317.         /*Get a side vector*/
  7318.         CROSS(forwardVector, upVector, side);
  7319.  
  7320.         /*Make the change rotation matrix by rows from top to bottom*/
  7321.         rotDelta[0][0] = t * SQUARE(side[0]) + cp;
  7322.         rotDelta[0][1] = t * side[0] * side[1] + sp * side[2];
  7323.         rotDelta[0][2] = t * side[0] * side[2] - sp * side[1];
  7324.         rotDelta[0][3] = 0.0;
  7325.     
  7326.         rotDelta[1][0] = t * side[0] * side[1] - sp * side[2];
  7327.         rotDelta[1][1] = t * SQUARE(side[1]) + cp;
  7328.         rotDelta[1][2] = t * side[1] * side[2] + sp * side[0];
  7329.         rotDelta[1][3] = 0.0;
  7330.     
  7331.         rotDelta[2][0] = t * side[0] * side[2] + sp * side[1];
  7332.         rotDelta[2][1] = t * side[1] * side[2] - sp * side[0];
  7333.         rotDelta[2][2] = t * SQUARE(side[2]) + cp;
  7334.         rotDelta[2][3] = 0.0;
  7335.     
  7336.         rotDelta[3][0] = 0.0;
  7337.         rotDelta[3][1] = 0.0;
  7338.         rotDelta[3][2] = 0.0;
  7339.         rotDelta[3][3] = 1.0;
  7340.     
  7341.         /*Rotate forward and up down through pitch*/
  7342.  
  7343.         MATBYVECTOR(rotDelta, forwardVector, temp);
  7344.         VECCOPY(forwardVector, temp);
  7345.  
  7346.         MATBYVECTOR(rotDelta, upVector, temp);
  7347.         VECCOPY(upVector, temp);
  7348.     }
  7349.     }
  7350.  
  7351.     var = GetVar(observer, ROLL);
  7352.     if (var)
  7353.     {
  7354.     phi = -GetReal(var);
  7355.     if (phi != 0.0)
  7356.     {
  7357.         /*Rotate around -roll*/
  7358.         sp = rsin(phi);
  7359.         cp = rcos(phi);
  7360.         t = 1.0 - cp;
  7361.     
  7362.         /*Make the change rotation matrix by rows from top to bottom*/
  7363.         rotDelta[0][0] = t * SQUARE(forwardVector[0]) + cp;
  7364.         rotDelta[0][1] = t * forwardVector[0] * forwardVector[1] + sp * forwardVector[2];
  7365.         rotDelta[0][2] = t * forwardVector[0] * forwardVector[2] - sp * forwardVector[1];
  7366.         rotDelta[0][3] = 0.0;
  7367.     
  7368.         rotDelta[1][0] = t * forwardVector[0] * forwardVector[1] - sp * forwardVector[2];
  7369.         rotDelta[1][1] = t * SQUARE(forwardVector[1]) + cp;
  7370.         rotDelta[1][2] = t * forwardVector[1] * forwardVector[2] + sp * forwardVector[0];
  7371.         rotDelta[1][3] = 0.0;
  7372.     
  7373.         rotDelta[2][0] = t * forwardVector[0] * forwardVector[2] + sp * forwardVector[1];
  7374.         rotDelta[2][1] = t * forwardVector[1] * forwardVector[2] - sp * forwardVector[0];
  7375.         rotDelta[2][2] = t * SQUARE(forwardVector[2]) + cp;
  7376.         rotDelta[2][3] = 0.0;
  7377.     
  7378.         rotDelta[3][0] = 0.0;
  7379.         rotDelta[3][1] = 0.0;
  7380.         rotDelta[3][2] = 0.0;
  7381.         rotDelta[3][3] = 1.0;
  7382.     
  7383.         /*Rotate up around through roll*/
  7384.         MATBYVECTOR(rotDelta, upVector, temp);
  7385.         VECCOPY(upVector, temp);
  7386.     }
  7387.     }
  7388. }
  7389.  
  7390. #ifdef PROTO
  7391. void GetObserverLocation(real *location, ObjPtr observer)
  7392. #else
  7393. void GetObserverLocation(location, observer)
  7394. real *location;
  7395. ObjPtr observer;
  7396. #endif
  7397. /*Gets an observer's location*/
  7398. {
  7399.     ObjPtr var;
  7400.     var = GetVar(observer, LOCATION);
  7401.     if (var)
  7402.     {
  7403.     Array2CArray(location, var);
  7404.     }
  7405.     else
  7406.     {
  7407.     location[0] = 0.0;
  7408.     location[1] = 0.0;
  7409.     location[2] = INITEYEDIST;
  7410.     }
  7411. }
  7412.  
  7413. #ifdef PROTO
  7414. void GetFocusPoint(real *focusPoint, ObjPtr observer)
  7415. #else
  7416. void GetFocusPoint(focusPoint, observer)
  7417. real *focusPoint;
  7418. ObjPtr observer;
  7419. #endif
  7420. /*Gets the focus point of an observer, adjusted for roll and pitch*/
  7421. {
  7422.     real forward[3], up[3];
  7423.     ObjPtr var;
  7424.     real focusDist;
  7425.  
  7426.     GetAdjustedVectors(forward, up, observer);
  7427.     GetObserverLocation(focusPoint, observer);
  7428.     var = GetVar(observer, FOCUSDIST);
  7429.     if (var)
  7430.     {
  7431.     focusDist = GetReal(var);
  7432.     }
  7433.     else
  7434.     {
  7435.     focusDist = INITEYEDIST;
  7436.     }
  7437.     focusPoint[0] += focusDist * forward[0];
  7438.     focusPoint[1] += focusDist * forward[1];
  7439.     focusPoint[2] += focusDist * forward[2];
  7440. }
  7441.  
  7442. ObjPtr NewObserver()
  7443. /*Returns a new observer*/
  7444. {
  7445.     ObjPtr retVal;
  7446.     ObjPtr name;
  7447.  
  7448.     if (oneObserver)
  7449.     {
  7450.     if (commonObserver) return commonObserver;
  7451.     }
  7452.     retVal = NewObject(observerClass, 0);
  7453.     if (oneObserver)
  7454.     {
  7455.     commonObserver = retVal;
  7456.     }
  7457.     name = GetVar(observerClass, NAME);
  7458.     if (name)
  7459.     {
  7460.     sprintf(tempStr, "%s %d", GetString(name), ++observerNum);
  7461.     }
  7462.     else
  7463.     {
  7464.     sprintf(tempStr, "Observer %d", ++observerNum);
  7465.     }
  7466.     name = NewString(tempStr);
  7467.     SetVar(retVal, NAME, name);
  7468.     SetVar(retVal, SPACES, NewList());
  7469.  
  7470.     return retVal;
  7471. }
  7472.  
  7473. ObjPtr NewRenderer()
  7474. /*Returns a new renderer*/
  7475. {
  7476.     ObjPtr retVal;
  7477.     ObjPtr name;
  7478.  
  7479.     if (oneRenderer)
  7480.     {
  7481.     if (commonRenderer) return commonRenderer;
  7482.     }
  7483.     retVal = NewObject(rendererClass, 0);
  7484.     if (oneRenderer)
  7485.     {
  7486.     commonRenderer = retVal;
  7487.     }
  7488.     name = GetVar(rendererClass, NAME);
  7489.     if (name)
  7490.     {
  7491.     sprintf(tempStr, "%s %d", GetString(name), ++rendererNum);
  7492.     }
  7493.     else
  7494.     {
  7495.     sprintf(tempStr, "Renderer %d", ++rendererNum);
  7496.     }
  7497.     name = NewString(tempStr);
  7498.     SetVar(retVal, NAME, name);
  7499.     SetVar(retVal, SPACES, NewList());
  7500.  
  7501.     return retVal;
  7502. }
  7503.  
  7504. static ObjPtr CloneObserver(observer)
  7505. ObjPtr observer;
  7506. /*Clones observer*/
  7507. {
  7508.     Bool oldOneObserver;
  7509.     ObjPtr retVal;
  7510.  
  7511.     oldOneObserver = oneObserver;
  7512.     oneObserver = false;
  7513.     retVal = NewObserver();
  7514.     oneObserver = oldOneObserver;
  7515.  
  7516.     CopyVar(retVal, observer, ROTAXIS);
  7517.     CopyVar(retVal, observer, ROTSPEED);    
  7518.     CopyVar(retVal, observer, VIEWFIELD);
  7519.     CopyVar(retVal, observer, VIEWCLIP);
  7520.     CopyVar(retVal, observer, LOCATION);
  7521.     CopyVar(retVal, observer, FORWARDVECTOR);
  7522.     CopyVar(retVal, observer, FOCUSDIST);
  7523.     CopyVar(retVal, observer, ROLL);
  7524.     CopyVar(retVal, observer, PITCH);
  7525.     CopyVar(retVal, observer, AIRSPEED);
  7526.  
  7527.     return retVal;
  7528. }
  7529.  
  7530. static ObjPtr CloneClock(clock)
  7531. ObjPtr clock;
  7532. {
  7533.     ObjPtr retVal;
  7534.     Bool oldOneClock;
  7535.  
  7536.     oldOneClock = oneClock;
  7537.     oneClock = false;
  7538.     retVal = NewClock();
  7539.     oneClock = oldOneClock;
  7540.  
  7541.     CopyVar(retVal, clock, CYCLECLOCK);
  7542.     CopyVar(retVal, clock, TIMESTEPS);
  7543.     CopyVar(retVal, clock, DATASETS);
  7544.     CopyVar(retVal, clock, TIME);
  7545.  
  7546.     return retVal;
  7547. }
  7548.  
  7549. static ObjPtr CloneRenderer(renderer)
  7550. ObjPtr renderer;
  7551. /*Clones renderer*/
  7552. {
  7553.     Bool oldOneRenderer;
  7554.     ObjPtr retVal;
  7555.  
  7556.     oldOneRenderer = oneRenderer;
  7557.     oneRenderer = false;
  7558.     retVal = NewRenderer();
  7559.     oneRenderer = oldOneRenderer;
  7560.  
  7561.     CopyVar(retVal, renderer, RENDERTYPE);
  7562.     CopyVar(retVal, renderer, FILTERTYPE);
  7563.  
  7564.     return retVal;
  7565. }
  7566.  
  7567. #if 0
  7568. static ObjPtr DrawExtraObserverIcon(icon, x, y)
  7569. ObjPtr icon;
  7570. int x, y;
  7571. /*Draws the extra stuff for an observer icon*/
  7572. {
  7573.     ObjPtr space, observers;
  7574.     space = GetObjectVar("DrawExtraObserverIcon", icon, SPACE);
  7575.     if (!space)
  7576.     {
  7577.     return ObjFalse;
  7578.     }
  7579.     observers = GetListVar("DrawExtraObserverIcon", space, OBSERVERS);
  7580.     if (!observers)
  7581.     {
  7582.     return ObjFalse;
  7583.     }
  7584.     if (GetVar(icon, REPOBJ) == LISTOF(observers) -> thing)
  7585.     {
  7586.     FrameUIRect(x - ICONSIZE / 2 - 4, x + ICONSIZE / 2 + 4, y - ICONSIZE / 2 - ICONSHADOW - 4, y + ICONSIZE / 2 + 4, UIREC);
  7587.     SetUIColor(UIBLACK);
  7588.     }
  7589.     return ObjTrue;
  7590. }
  7591. #endif
  7592.  
  7593. ObjPtr ReshapeSpacePanel(object, ol, or, ob, ot, left, right, bottom, top)
  7594. ObjPtr object;
  7595. int ol, or, ob, ot;
  7596. int left, right, bottom, top;
  7597. /*Reshapes a space panel, which used to exist within owner with edges ol, or, ob, ot
  7598.   to one which exists within owner with edges left, right, bottom, top.*/
  7599. {
  7600.     ObjPtr boundsArray;
  7601.     ObjPtr stickyInt;
  7602.     real bounds[4];
  7603.     real oldWidth, oldHeight;    /*Old width and height*/
  7604.     Bool sideLocked[4];        /*True iff side is locked*/
  7605.     Bool xStretch, yStretch;    /*Stretchiness in x and y*/
  7606.     int stickiness;        /*Side stickiness of the object*/
  7607.     real oldBounds[4];        /*Old bounds of the object*/
  7608.     ObjPtr contents;        /*Contents of the object, if any*/
  7609.     real wr, hr;        /*Width and height ratios*/
  7610.  
  7611.     /*Reshape the panel itself*/
  7612.     wr = ((real) (right - left)) / ((real) (or - ol));
  7613.     hr = ((real) (top - bottom)) / ((real) (ot - ob));
  7614.  
  7615.     MakeVar(object, BOUNDS);
  7616.     boundsArray = GetVar(object, BOUNDS);
  7617.     if (!boundsArray || !IsRealArray(boundsArray) || RANK(boundsArray) != 1 ||
  7618.     DIMS(boundsArray)[0] != 4)
  7619.     {
  7620.     return ObjFalse;
  7621.     }
  7622.     Array2CArray(bounds, boundsArray);
  7623.     Array2CArray(oldBounds, boundsArray);
  7624.     oldWidth = bounds[1] - bounds[0];
  7625.     oldHeight = bounds[3] - bounds[2];
  7626.  
  7627.     /*Get the object's stickiness*/
  7628.     stickyInt = GetVar(object, STICKINESS);
  7629.     if (stickyInt && IsInt(stickyInt))
  7630.     {
  7631.     stickiness = GetInt(stickyInt);
  7632.     }
  7633.     else
  7634.     {
  7635.     stickiness = 0;
  7636.     }
  7637.  
  7638.     if ((stickiness & STICKYLEFT) || (stickiness & FLOATINGLEFT))
  7639.     {
  7640.     if (stickiness & FLOATINGLEFT)
  7641.     {
  7642.         bounds[0] = (bounds[0] - ol) * wr + left;
  7643.     }
  7644.     else
  7645.     {
  7646.         bounds[0] += left - ol;
  7647.     }
  7648.     if (!((stickiness & STICKYRIGHT) || (stickiness & FLOATINGRIGHT)))
  7649.     {
  7650.         bounds[1] = bounds[0] + oldWidth;
  7651.     }
  7652.     }
  7653.     if ((stickiness & STICKYRIGHT) || (stickiness & FLOATINGRIGHT))
  7654.     {
  7655.     if (stickiness & FLOATINGRIGHT)
  7656.     {
  7657.         bounds[1] = (bounds[1] - ol) * wr + left;
  7658.     }
  7659.     else
  7660.     {
  7661.         bounds[1] += right - or;
  7662.     }
  7663.     if (!((stickiness & STICKYLEFT) || (stickiness & FLOATINGLEFT)))
  7664.     {
  7665.         bounds[0] = bounds[1] - oldWidth;
  7666.     }
  7667.     }
  7668.  
  7669.     if ((stickiness & STICKYBOTTOM) || (stickiness & FLOATINGBOTTOM))
  7670.     {
  7671.     if (stickiness & FLOATINGBOTTOM)
  7672.     {
  7673.         bounds[2] = (bounds[2] - ob) * hr + bottom;
  7674.     }
  7675.     else
  7676.     {
  7677.         bounds[2] += bottom - ob;
  7678.     }
  7679.     if (!((stickiness & STICKYTOP) || (stickiness & FLOATINGTOP)))
  7680.     {
  7681.         bounds[3] = bounds[2] + oldHeight;
  7682.     }
  7683.     }
  7684.     if ((stickiness & STICKYTOP) || (stickiness & FLOATINGTOP))
  7685.     {
  7686.     if (stickiness & FLOATINGTOP)
  7687.     {
  7688.         bounds[3] = (bounds[3] - ob) * hr + bottom;
  7689.     }
  7690.     else
  7691.     {
  7692.         bounds[3] += top - ot;
  7693.     }
  7694.     if (!((stickiness & STICKYBOTTOM) || (stickiness & FLOATINGBOTTOM)))
  7695.     {
  7696.         bounds[2] = bounds[3] - oldHeight;
  7697.     }
  7698.     }
  7699.  
  7700.     /*We've got a new bounds, put it back*/
  7701.     boundsArray = NewRealArray(1, 4L);
  7702.     CArray2Array(boundsArray, bounds);
  7703.     SetVar(object, BOUNDS, boundsArray);
  7704.  
  7705.     /*If there are some contents to this, reshape them*/
  7706.     contents = GetVar(object, CONTENTS);
  7707.     if (contents && IsList(contents))
  7708.     {
  7709.     ThingListPtr runner;
  7710.     real oldCenterX, oldCenterY, centerX, centerY;
  7711.     real enlargement;
  7712.     
  7713.     oldCenterX = ((real) (oldBounds[1] - oldBounds[0])) * 0.5;
  7714.     oldCenterY = ((real) (oldBounds[3] - oldBounds[2])) * 0.5;
  7715.     centerX = ((real) (bounds[1] - bounds[0])) * 0.5;
  7716.     centerY = ((real) (bounds[3] - bounds[2])) * 0.5;
  7717.     enlargement = ((real) (bounds[3] - bounds[2])) /
  7718.               ((real) (oldBounds[3] - oldBounds[2]));
  7719.  
  7720.     runner = LISTOF(contents);
  7721.     while (runner)
  7722.     {
  7723.         ObjPtr var;
  7724.         real point[2];
  7725.         object = runner -> thing;
  7726.         MakeVar(object, BOUNDS);
  7727.         boundsArray = GetVar(object, BOUNDS);
  7728.         if (boundsArray && IsRealArray(boundsArray) && RANK(boundsArray) == 1 &&
  7729.         DIMS(boundsArray)[0] == 4)
  7730.         {
  7731.         /*Valid bounds.  Change them.*/
  7732.         Array2CArray(bounds, boundsArray);
  7733.  
  7734.         bounds[0] = centerX + (bounds[0] + HANDLESIZE / 2 - oldCenterX) * enlargement - HANDLESIZE / 2;
  7735.         bounds[1] = centerX + (bounds[1] - HANDLESIZE / 2 - oldCenterX) * enlargement + HANDLESIZE / 2;
  7736.         bounds[2] = centerY + (bounds[2] + HANDLESIZE / 2 - oldCenterY) * enlargement - HANDLESIZE / 2;
  7737.         bounds[3] = centerY + (bounds[3] - HANDLESIZE / 2 - oldCenterY) * enlargement + HANDLESIZE / 2;
  7738.  
  7739.         /*We've got a new bounds, put it back*/
  7740.         boundsArray = NewRealArray(1, 4L);
  7741.         CArray2Array(boundsArray, bounds);
  7742.         SetVar(object, BOUNDS, boundsArray);
  7743.         }
  7744.  
  7745.         var = GetVar(object, STARTPOINT);
  7746.         if (var && IsRealArray(var) && RANK(var) == 1 && DIMS(var)[0] == 2)
  7747.         {
  7748.         Array2CArray(point, var);
  7749.         point[0] = centerX + (point[0] - oldCenterX) * enlargement;
  7750.         point[1] = centerY + (point[1] - oldCenterY) * enlargement;
  7751.         var = NewRealArray(1, 2L);
  7752.         CArray2Array(var, point);
  7753.         SetVar(object, STARTPOINT, var);
  7754.         }
  7755.         
  7756.         var = GetVar(object, ENDPOINT);
  7757.         if (var && IsRealArray(var) && RANK(var) == 1 && DIMS(var)[0] == 2)
  7758.         {
  7759.         Array2CArray(point, var);
  7760.         point[0] = centerX + (point[0] - oldCenterX) * enlargement;
  7761.         point[1] = centerY + (point[1] - oldCenterY) * enlargement;
  7762.         var = NewRealArray(1, 2L);
  7763.         CArray2Array(var, point);
  7764.         SetVar(object, ENDPOINT, var);
  7765.         }
  7766.         
  7767.         runner = runner -> next;
  7768.     }
  7769.     }
  7770.     return ObjTrue;
  7771. }
  7772.  
  7773. PrintCurMatrix(s)
  7774. char *s;
  7775.     {
  7776.         Matrix haha;
  7777.         int i, j;
  7778.         printf("%s\n", s);
  7779.         getmatrix(haha);
  7780.         for (j = 0; j < 4; ++j)
  7781.         {
  7782.         for (i = 0; i < 4; ++i)
  7783.         {
  7784.             printf("%15g ", haha[j][i]);
  7785.         }
  7786.         printf("\n");
  7787.         }
  7788.         printf("\n");
  7789.     }
  7790.  
  7791. static ObjPtr DrawSpacePanel(object)
  7792. ObjPtr object;
  7793. /*Draws a space panel*/
  7794. {
  7795. #ifdef GRAPHICS
  7796.     int l, r, b, t;
  7797.     ObjPtr backColor, borderType;
  7798.     ObjPtr frontPanel, space;
  7799.  
  7800.     /*Only really draw it if we're drawing to the screen, so that
  7801.       PostScript image stuff works properly*/
  7802.     if (drawingMode != DRAW_SCREEN)
  7803.     {
  7804.     return ObjTrue;
  7805.     }
  7806.  
  7807.     space = GetVar(object, SPACE);
  7808.     if (space)
  7809.     {
  7810.     frontPanel = GetVar(space, FRONTPANEL);
  7811.     if (frontPanel && (frontPanel != object))
  7812.     {
  7813.         /*I'm not the front panel*/
  7814.         if (GetVar(frontPanel, BACKGROUND) || GetPredicate(frontPanel, TEMPOBSCURED))
  7815.         {
  7816.         /*Front panel has a background.  I don't need to draw.*/
  7817.         return ObjTrue;
  7818.         }
  7819.     }
  7820.     }
  7821.  
  7822.     Get2DIntBounds(object, &l, &r, &b, &t);
  7823.     if (IsDrawingRestricted(l, r, b, t)) return ObjFalse;
  7824.  
  7825.     /*Setup the new viewport*/
  7826.     StartPanel(l, r, b, t);
  7827.  
  7828.     /*Get the color, if any, and draw it*/
  7829.     backColor = GetVar(object, BACKGROUND);
  7830.     if (backColor)
  7831.     {
  7832.     if (GetPredicate(object, BACKNOTNEEDED))
  7833.     {
  7834.         SetVar(object, BACKNOTNEEDED, ObjFalse);
  7835.     }
  7836.     else
  7837.     {
  7838.         SetObjectColor(backColor);
  7839.         FillRect(0, r - l, 0, t - b);
  7840.         /*KLUDGE for IBM*/
  7841.         FrameRect(0, r - l, 0, t - b);
  7842.     }
  7843.     }
  7844.     else if (GetPredicate(object, TEMPOBSCURED))
  7845.     {
  7846.     FillUIRect(0, r - l, 0, t - b, UIBLACK);
  7847. #if MACHINE == RS6000
  7848.         /*KLUDGE for IBM*/
  7849.         FrameRect(0, r - l, 0, t - b);
  7850. #endif
  7851.     }
  7852.     SetVar(object, BACKNOTNEEDED, ObjFalse);
  7853.  
  7854.     borderType = GetVar(object, BORDERTYPE);
  7855.     if (borderType)
  7856.     {
  7857.     FrameUIRect(0, r - l - 1, 0, t - b - 1, UIBLACK);
  7858.     }
  7859.  
  7860.     /*Draw the grid if need be*/
  7861.     if (GetPredicate(object, SHOWGRID))
  7862.     {
  7863.     real x, y;
  7864.     SetScreenGrid(object);
  7865.     SetUIColor(UIWHITE);
  7866.  
  7867.     for (x = GRIDX(-xGrid); x < (r - l) + xGrid; x = GRIDX(x + xGrid))
  7868.     {
  7869.         for (y = GRIDY(-yGrid); y < (t - b) + yGrid; y = GRIDY(y + yGrid))
  7870.         {
  7871.         FillUIRect(x - 1, x + 1, y - 1, y + 1, UIBLACK);
  7872.         FrameUIRect(x - 1, x + 1, y - 1, y + 1, UIWHITE);
  7873.         }
  7874.     }
  7875.     }
  7876.  
  7877.     /*Draw the contents of the panel*/
  7878.     DrawList(GetVar(object, CONTENTS));
  7879.     StopPanel();
  7880. #endif
  7881.  
  7882.     return NULLOBJ;
  7883. }
  7884.  
  7885. ObjPtr ShowSpaceControls(space, windowName)
  7886. ObjPtr space;
  7887. char *windowName;
  7888. /*Makes a new control window to control space*/
  7889. {
  7890.     WinInfoPtr dialogExists, controlWindow;
  7891.     ObjPtr whichDialog;
  7892.     ObjPtr name;
  7893.     int left, right, bottom, top;
  7894.  
  7895.     name = GetVar(space, NAME);
  7896.  
  7897.     whichDialog = NewString("Controls");
  7898.     dialogExists = DialogExists((WinInfoPtr) space, whichDialog);
  7899.     if (name)
  7900.     {
  7901.     strncpy(tempStr, GetString(name), TEMPSTRSIZE);
  7902.     tempStr[TEMPSTRSIZE] = 0;
  7903.     }
  7904.     else
  7905.     {
  7906.     strcpy(tempStr, "Space");
  7907.     }
  7908.     GetTemplateBounds(SpaceTemplate, "Panel", &left, &right, &bottom, &top);
  7909.     controlWindow = GetDialog((WinInfoPtr) space, whichDialog, tempStr, 
  7910.     right - left, top - bottom, right - left, top - bottom, WINUI);
  7911.  
  7912.     if (!dialogExists)
  7913.     {
  7914.     ObjPtr panel, contents, checkBox;
  7915.  
  7916.     SetVar((ObjPtr) controlWindow, REPOBJ, space);
  7917.  
  7918.     /*Add controls*/
  7919.     SetVar((ObjPtr) controlWindow, HELPSTRING,
  7920.         NewString("This window shows controls that affect attributes \
  7921. of a 3-dimensional space."));
  7922.  
  7923.     /*Add in a panel*/
  7924.     panel = TemplatePanel(SpaceTemplate, "Panel");
  7925.     if (!panel)
  7926.     {
  7927.         return ObjFalse;
  7928.     }
  7929.     contents = GetVar((ObjPtr) controlWindow, CONTENTS);
  7930.     PrefixList(contents, panel);
  7931.     SetVar(panel, PARENT, (ObjPtr) controlWindow);
  7932.  
  7933.     contents = GetVar((ObjPtr) panel, CONTENTS);
  7934.  
  7935.     checkBox = TemplateCheckBox(SpaceTemplate, "Show Rotation Guides", GetPredicate(space, SHOWROTGUIDES));
  7936.     SetVar(checkBox, PARENT, panel);
  7937.     PrefixList(contents, checkBox);
  7938.     if (!GetVar(space, SHOWROTGUIDES)) SetVar(space, SHOWROTGUIDES, ObjFalse);
  7939.     AssocDirectControlWithVar(checkBox, space, SHOWROTGUIDES);
  7940.     SetVar(checkBox, HELPSTRING, NewString(
  7941.         "This controls whether rotation guides are shown in the space.  If the check box is \
  7942. on, a wire frame sphere will appear in the space.  The sphere represents the virtual \
  7943. trackball you can press and move using the middle mouse button"));
  7944.  
  7945.  
  7946.     checkBox = TemplateCheckBox(SpaceTemplate, "Show Motion Guides", GetPredicate(space, SHOWMOTGUIDES));
  7947.     SetVar(checkBox, PARENT, panel);
  7948.     PrefixList(contents, checkBox);
  7949.     if (!GetVar(space, SHOWMOTGUIDES)) SetVar(space, SHOWMOTGUIDES, ObjFalse);
  7950.     AssocDirectControlWithVar(checkBox, space, SHOWMOTGUIDES);
  7951.     SetVar(checkBox, HELPSTRING, NewString(
  7952.         "This controls whether motion guides are shown in the space.  If it is \
  7953. on, a wire frame lattice will appear within the space, aligned to space coordinates."));
  7954.  
  7955.     }
  7956.  
  7957.     return (ObjPtr) dialogExists;
  7958. }
  7959.  
  7960. static ObjPtr MakeSpaceAppearance(space)
  7961. ObjPtr space;
  7962. {
  7963.     SetVar(space, APPEARANCE, ObjTrue);
  7964.     ImInvalid(space);
  7965.     return ObjTrue;
  7966. }
  7967.  
  7968. static ObjPtr MakeSpaceTime(space)
  7969. ObjPtr space;
  7970. {
  7971.     ObjPtr clock, var;
  7972.     MakeVar(space, CLOCK);
  7973.     clock = GetVar(space, CLOCK);
  7974.     if (clock)
  7975.     {
  7976.     MakeVar(clock, TIME);
  7977.     var = GetVar(clock, TIME);
  7978.     if (var)
  7979.     {
  7980.         SetVar(space, TIME, var);
  7981.         return ObjTrue;
  7982.     }
  7983.     }
  7984.     SetVar(space, TIME, NewReal(0.0));
  7985.     return ObjTrue;
  7986. }
  7987.  
  7988. static ObjPtr ExpressObserverPlacement(track, frame)
  7989. ObjPtr track;
  7990. int frame;
  7991. /*Expresses the placement of observer placement track track at frame*/
  7992. {
  7993.     ObjPtr observer;
  7994.     Quaternion qrot;
  7995.     real location[3];
  7996.     ObjPtr var;
  7997.  
  7998.     /*Get the observer*/
  7999.     observer = GetObjectVar("ExpressObserverPlacement", track, REPOBJ);
  8000.     if (!observer) return ObjFalse;
  8001.  
  8002.     /*Find where it should be*/
  8003.     if (TweenObserverPlacement(track, frame, qrot, location))
  8004.     {
  8005.     /*Set its little variables*/
  8006.     var = NewRealArray(1, 4L);
  8007.     CArray2Array(var, qrot);
  8008.     SetVar(observer, ROTQUAT, var);
  8009.     var = NewRealArray(1, 3L);
  8010.     CArray2Array(var, location);
  8011.     SetVar(observer, LOCATION, var);
  8012.     }
  8013.  
  8014.     ImInvalid(observer);
  8015.  
  8016.     return ObjTrue;
  8017. }
  8018.  
  8019. static ObjPtr MakePlacementJoints(track)
  8020. ObjPtr track;
  8021. /*Makes the placement interpolateion joints for track*/
  8022. {
  8023.     ObjPtr *keyframes;        /*Array of all keyframes in track*/
  8024.     ObjPtr *joints;        /*Array of all joints in track*/
  8025.     ObjPtr var;            /*Random variable*/
  8026.     long nJoints;        /*Dimension of joints*/
  8027.     long nKeyframes;        /*Dimension of keyframes, 1+nJoints*/
  8028.     long k;            /*Counter for joints*/
  8029.     ObjPtr snap1, snap2;    /*Snapshots*/
  8030.     real weight;        /*Weight at a joint*/
  8031.     long deltaTime;        /*Delta time at a joint in frames*/
  8032.     real w1, w2;        /*Different weights*/
  8033.     real dt1, dt2;        /*Different delta times*/
  8034.     real rs1, rs2;        /*Different rotational speeds*/
  8035.     Quaternion q1, q2, qr;    /*Quaternions for calculation*/
  8036.     real axis[3];        /*Axis of a rotation*/
  8037.     real rotSpeed;        /*Rotational speed*/
  8038.     real speed;            /*Linear speed*/
  8039.     real radius;        /*Radius to pivot*/
  8040.     real distance1, distance2;    /*Distances between quaternions*/
  8041.     real pivot[3];        /*Pivot for a joint*/
  8042.     Quaternion deltaRot;    /*Delta rotation*/
  8043.     real axisLength;        /*Length of a rotation axis*/
  8044.     real loc1[3], loc2[3];    /*Locations*/
  8045.     real lloc1[3], lloc2[3];    /*Location in local (disc) coordinates*/
  8046.  
  8047.     /*Get the keyframes*/
  8048.     var = GetVar(track, KEYFRAMES);
  8049.     if (!var || DIMS(var)[0] < 2)
  8050.     {
  8051.     /*Don't need to make any joints*/
  8052.     SetVar(track, JOINTS, NULLOBJ);
  8053.     return ObjTrue;
  8054.     }
  8055.     keyframes = ELEMENTS(var);
  8056.     nKeyframes = DIMS(var)[0];
  8057.  
  8058.     /*Go through and make sure that the correct quaternions are used on
  8059.       each keyframe*/
  8060.     snap1 = GetObjectVar("MakePlacementJoints", keyframes[0], SNAPSHOT);
  8061.     if (!snap1) return NULLOBJ;
  8062.     var = GetFixedArrayVar("MakePlacementJoints", snap1, ROTQUAT, 1, 4L);
  8063.     if (!var) return NULLOBJ;
  8064.     Array2CArray(q1, var);
  8065.     for (k = 1; k < nKeyframes; ++k)
  8066.     {
  8067.     snap2 = GetObjectVar("MakePlacementJoints", keyframes[k], SNAPSHOT);
  8068.     if (!snap2) return NULLOBJ;
  8069.     var = GetFixedArrayVar("MakePlacementJoints", snap2, ROTQUAT, 1, 4L);
  8070.     if (!var) return NULLOBJ;
  8071.     Array2CArray(q2, var);
  8072.  
  8073.     distance1 = SQUARE(q2[0] - q1[0]) +
  8074.             SQUARE(q2[1] - q1[1]) +
  8075.             SQUARE(q2[2] - q1[2]) +
  8076.             SQUARE(q2[3] - q1[3]);
  8077.     distance2 = SQUARE(-q2[0] - q1[0]) +
  8078.             SQUARE(-q2[1] - q1[1]) +
  8079.             SQUARE(-q2[2] - q1[2]) +
  8080.             SQUARE(-q2[3] - q1[3]);
  8081.  
  8082.     if (distance2 < distance1)
  8083.     {
  8084.         /*Have to negate second quaternion*/
  8085.         q2[0] = -q2[0];
  8086.         q2[1] = -q2[1];
  8087.         q2[2] = -q2[2];
  8088.         q2[3] = -q2[3];
  8089.  
  8090.         var = NewRealArray(1, 4L);
  8091.         CArray2Array(var, q2);
  8092.         SetVar(snap2, ROTQUAT, var);
  8093.     }
  8094.  
  8095.     q1[0] = q2[0];
  8096.     q1[1] = q2[1];
  8097.     q1[2] = q2[2];
  8098.     q1[3] = q2[3];
  8099.  
  8100.     snap1 = snap2;
  8101.     }
  8102.  
  8103.     /*Create the joints*/
  8104.     nJoints = nKeyframes - 1;
  8105.     var = NewArray(AT_OBJECT, 1, &nJoints);
  8106.     joints = ELEMENTS(var);
  8107.     SetVar(track, JOINTS, var);
  8108.  
  8109.     /*Fill them with null joints for now*/ 
  8110.     for (k = 0; k < nJoints; ++k)
  8111.     {
  8112.     joints[k] = NewObject(jointClass, 0L);
  8113.     }
  8114.  
  8115.     /*Go through and calculate the delta rotation, delta time, and 
  8116.       initial weight of each joint*/
  8117.     
  8118.     snap1 = GetObjectVar("MakePlacementJoints", keyframes[0], SNAPSHOT);
  8119.     if (!snap1) return NULLOBJ;
  8120.     for (k = 0; k < nJoints; ++k)
  8121.     {
  8122.     snap2 = GetObjectVar("MakePlacementJoints", keyframes[k + 1], SNAPSHOT);
  8123.     if (!snap2) return NULLOBJ;
  8124.  
  8125.     var = GetArrayVar("MakePlacementJoints", snap1, ROTQUAT);
  8126.     if (!var) return NULLOBJ;
  8127.     Array2CArray(q1, var);
  8128.  
  8129.     var = GetArrayVar("MakePlacementJoints", snap2, ROTQUAT);
  8130.     if (!var) return NULLOBJ;
  8131.     Array2CArray(q2, var);
  8132.  
  8133.     /*Make delta rotation*/
  8134.     QDelta(q1, q2, qr);
  8135.  
  8136.     /*Make delta time*/
  8137.     var = GetIntVar("MakePlacementJoints", keyframes[k + 1], WHICHFRAME);
  8138.     if (!var) return NULLOBJ;
  8139.     deltaTime = GetInt(var);
  8140.     var = GetIntVar("MakePlacementJoints", keyframes[k], WHICHFRAME);
  8141.     if (!var) return NULLOBJ;
  8142.     deltaTime -= GetInt(var);
  8143.  
  8144.     /*Make weight*/
  8145.     weight = SQUARE(qr[1]) +
  8146.          SQUARE(qr[2]) +
  8147.          SQUARE(qr[3]);
  8148.  
  8149.     /*Put the delta rot, delta time, and weight in the joint*/
  8150.     var = NewRealArray(1, 4L);
  8151.     CArray2Array(var, qr);
  8152.     SetVar(joints[k], DELTAROT, var);
  8153.     SetVar(joints[k], DELTATIME, NewReal((real) deltaTime));
  8154.     SetVar(joints[k], WEIGHT, NewReal(weight));
  8155.  
  8156.     printf("joint %d:\n    delta = (%g, [%g %g %g])\n    weight = %g\n    dt = %ld\n",
  8157.         k,
  8158.         qr[0], qr[1], qr[2], qr[3],
  8159.         weight,
  8160.         deltaTime);
  8161.  
  8162.     snap1 = snap2;
  8163.     }
  8164.  
  8165.     /*Make consensus rotational velocity in radians per frame around an axis
  8166.       for all frames.*/
  8167.  
  8168.     /*First do the first frame.*/
  8169.     var = GetFixedArrayVar("MakePlacementJoints", joints[0], DELTAROT, 1, 4L);
  8170.     if (!var) return NULLOBJ;
  8171.     Array2CArray(q1, var);
  8172.     QuaternionToRot(q1, axis, &rotSpeed);
  8173.     var = GetRealVar("MakePlacementJoints", joints[0], DELTATIME);
  8174.     if (!var) return NULLOBJ;
  8175.     rotSpeed /= GetReal(var);
  8176.     var = NewRealArray(1, 3L);
  8177.     CArray2Array(var, axis);
  8178.     SetVar(keyframes[0], ROTAXIS, var);
  8179.     SetVar(keyframes[0], ROTSPEED, NewReal(rotSpeed));
  8180.  
  8181.     /*Now do the last frame.*/
  8182.     var = GetFixedArrayVar("MakePlacementJoints", joints[nJoints - 1], DELTAROT, 1, 4L);
  8183.     if (!var) return NULLOBJ;
  8184.     Array2CArray(q1, var);
  8185.     QuaternionToRot(q1, axis, &rotSpeed);
  8186.     var = GetRealVar("MakePlacementJoints", joints[nJoints - 1], DELTATIME);
  8187.     if (!var) return NULLOBJ;
  8188.     rotSpeed /= GetReal(var);
  8189.     var = NewRealArray(1, 3L);
  8190.     CArray2Array(var, axis);
  8191.     SetVar(keyframes[nKeyframes - 1], ROTAXIS, var);
  8192.     SetVar(keyframes[nKeyframes - 1], ROTSPEED, NewReal(rotSpeed));
  8193.  
  8194.     /*Now do the remainder of the frames.  Make a consensus rotational 
  8195.       velocity at each keyframe:
  8196.     1) Get the individual delta rotations from the two joints that 
  8197.        share this keyframe.
  8198.     2) Do a weighted slerp based on the weights and time ratios
  8199.     3) Use time ratios to calculate rotational velocity.*/
  8200.  
  8201.     var = GetRealVar("MakePlacementJoints", joints[0], DELTATIME);
  8202.     if (!var) return NULLOBJ;
  8203.     dt1 = GetReal(var);
  8204.     var = GetFixedArrayVar("MakePlacementJoints", joints[0], DELTAROT, 1, 4L);
  8205.     if (!var) return NULLOBJ;
  8206.     Array2CArray(q1, var);
  8207.     var = GetRealVar("MakePlacementJoints", joints[0], WEIGHT);
  8208.     if (!var) return NULLOBJ;
  8209.     w1 = GetReal(var);
  8210.  
  8211.     for (k = 1; k < nKeyframes - 1; ++k)
  8212.     {
  8213.     var = GetRealVar("MakePlacementJoints", joints[k], DELTATIME);
  8214.     if (!var) return NULLOBJ;
  8215.     dt2 = GetReal(var);
  8216.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], DELTAROT, 1, 4L);
  8217.     if (!var) return NULLOBJ;
  8218.      Array2CArray(q2, var);
  8219.     var = GetRealVar("MakePlacementJoints", joints[k], WEIGHT);
  8220.     if (!var) return NULLOBJ;
  8221.     w2 = GetReal(var);
  8222.  
  8223.     if (dt1 <= 0.0 || dt2 <= 0.0 || w1 == 0.0 || w2 == 0.0)
  8224.     {
  8225.         /*Neither has weight, so weight evenly*/
  8226.         w1 = w2 = dt1 = dt2 = 1.0;
  8227.     }
  8228.  
  8229.     /*Do the weighted averaging*/
  8230.     Slerp(q1, q2, (w2 / dt2) / (w1 / dt1 + w2 / dt2), qr);
  8231.  
  8232.     /*Make the rotational axis*/
  8233.     QuaternionToRot(qr, axis, &rotSpeed);
  8234.     var = NewRealArray(1, 3L);
  8235.     CArray2Array(var, axis);
  8236.     SetVar(keyframes[k], ROTAXIS, var);
  8237.     
  8238.     /*Make the speed*/
  8239.     QuaternionToRot(q1, axis, &rs1);
  8240.     rs1 /= dt1;
  8241.     QuaternionToRot(q2, axis, &rs2);
  8242.     rs2 /= dt2;
  8243.     rotSpeed = rs1 + (rs2 - rs1) * (w2 / dt2) / (w1 / dt1 + w2 / dt2);
  8244.     SetVar(keyframes[k], ROTSPEED, NewReal(rotSpeed));
  8245.  
  8246.     dt1 = dt2;
  8247.     q1[0] = q2[0];
  8248.     q1[1] = q2[1];
  8249.     q1[2] = q2[2];
  8250.     q1[3] = q2[3];
  8251.     w1 = w2;
  8252.     }
  8253.  
  8254.     /*Print out revised keyframes*/
  8255.     for (k = 0; k < nKeyframes; ++k)
  8256.     {
  8257.     printf("keyframe %d\n", k);
  8258.     var = GetVar(keyframes[k], ROTSPEED);
  8259.     printf("    speed = %g\n", GetReal(var));
  8260.     var = GetVar(keyframes[k], ROTAXIS);
  8261.     Array2CArray(axis, var);
  8262.     printf("    axis = [%g %g %g]\n", axis[0], axis[1], axis[2]);
  8263.     }
  8264.  
  8265.     /*Make the pivots for every joint*/
  8266.     snap1 = GetObjectVar("MakePlacementJoints", keyframes[0], SNAPSHOT);
  8267.     if (!snap1) return NULLOBJ;
  8268.  
  8269.     var = GetFixedArrayVar("MakePlacementJoints", snap1, LOCATION, 1, 3L);
  8270.     if (!var) return NULLOBJ;
  8271.     Array2CArray(loc1, var);
  8272.  
  8273.     for (k = 0; k < nJoints; ++k)
  8274.     {
  8275.     snap2 = GetObjectVar("MakePlacementJoints", keyframes[k + 1], SNAPSHOT);
  8276.     if (!snap2) return NULLOBJ;
  8277.  
  8278.     var = GetFixedArrayVar("MakePlacementJoints", snap2, LOCATION, 1, 3L);
  8279.     if (!var) return NULLOBJ;
  8280.     Array2CArray(loc2, var);
  8281.  
  8282.     /*Find the length of the rotation axis*/
  8283.     var = GetRealVar("MakePlacementJoints", joints[k], WEIGHT);
  8284.     if (!var) return NULLOBJ;
  8285.     weight = GetReal(var);
  8286.     axisLength = sqrt(weight);
  8287.  
  8288.     /*Start off with a rotation center point between the two observer points*/
  8289.     pivot[0] = (loc1[0] + loc2[0]) * 0.5;
  8290.     pivot[1] = (loc1[1] + loc2[1]) * 0.5;
  8291.     pivot[2] = (loc1[2] + loc2[2]) * 0.5;
  8292.  
  8293.     if (axisLength <= 0.0)
  8294.     {
  8295.         /*There's no rotation, so arbitrarily use the center*/
  8296.     }
  8297.     else
  8298.     {
  8299.         real t[3];        /*Translation*/
  8300.         real r[3];        /*Radial displacement from center*/
  8301.         real d[3];        /*Displacement vector*/
  8302.         real rLength;    /*Length of radial direction vector*/
  8303.         real dLength;    /*Length of displacement vector*/
  8304.  
  8305.         /*Get rotation axis*/
  8306.         var = GetFixedArrayVar("MakePlacementJoints", joints[k], DELTAROT, 1, 4L);
  8307.         if (!var) return NULLOBJ;
  8308.         Array2CArray(deltaRot, var);
  8309.  
  8310.         /*Make normalized rotation axis direction*/
  8311.         axis[0] = deltaRot[1] / axisLength;
  8312.         axis[1] = deltaRot[2] / axisLength;
  8313.         axis[2] = deltaRot[3] / axisLength;
  8314.  
  8315.         /*Make translation vector*/
  8316.         t[0] = loc2[0] - loc1[0];
  8317.         t[1] = loc2[1] - loc1[1];
  8318.         t[2] = loc2[2] - loc1[2];
  8319.  
  8320.         /*Make a radial direction vector*/
  8321.         CROSS(t, axis, r);
  8322.  
  8323.         rLength = sqrt(SQUARE(r[0]) + SQUARE(r[1]) + SQUARE(r[2]));
  8324.         if (rLength <= 0.0)
  8325.         {
  8326.         /*There is no translation or it's all in the direction of
  8327.           rotation, stick with the center of observer*/
  8328.         }
  8329.         else
  8330.         {
  8331.         /*There is a translation, so we can make a stab at it*/
  8332.         real o;        /*Offset in r direction*/
  8333.  
  8334.         /*Normalize the r vector*/
  8335.         r[0] /= rLength;
  8336.         r[1] /= rLength;
  8337.         r[2] /= rLength;
  8338.  
  8339.         /*Cross a and r to get direction of displacement vector on disk*/
  8340.         CROSS(axis, r, d);
  8341.  
  8342.         /*Project the translation onto this vector*/
  8343.         dLength = DOT(t, d);
  8344.  
  8345.         /*Offset in r direction by dLength cos(theta / 2) / 
  8346.           2 sin(theta / 2).  Lucky us, we already have
  8347.           sin(theta / 2) in axisLength and cos(theta / 2) in the
  8348.           scalar part of the quaternion.*/
  8349.  
  8350.         o = dLength * deltaRot[0] / axisLength / 2.0;
  8351.  
  8352.         pivot[0] += r[0] * o;
  8353.         pivot[1] += r[1] * o;
  8354.         pivot[2] += r[2] * o;
  8355.  
  8356.         }
  8357.     }
  8358.  
  8359.     /*Stick the pivot in the joint*/
  8360.     var = NewRealArray(1, 3L);
  8361.     CArray2Array(var, pivot);
  8362.     SetVar(joints[k], PIVOT, var);
  8363.  
  8364.     snap1 = snap2;
  8365.     loc1[0] = loc2[0];
  8366.     loc1[1] = loc2[1];
  8367.     loc1[2] = loc2[2];
  8368.     }
  8369.  
  8370.     /*Print out the pivots*/
  8371.     for (k = 0; k < nJoints; ++k)
  8372.     {
  8373.     printf("Joint %d\n", k);
  8374.     var = GetVar(joints[k], PIVOT);
  8375.     Array2CArray(pivot, var);
  8376.     printf("    initial pivot = [%g %g %g]\n", pivot[0], pivot[1], pivot[2]);
  8377.     }
  8378.  
  8379.     /*Calculate the local location and desired velocity at the two 
  8380.     endpoints for each joint*/
  8381.  
  8382.     /*Get the first snapshot*/
  8383.     snap1 = GetObjectVar("MakePlacementJoints", keyframes[0], SNAPSHOT);
  8384.     if (!snap1) return NULLOBJ;
  8385.  
  8386.     /*Get the world location at the first snapshot*/
  8387.     var = GetFixedArrayVar("MakePlacementJoints", snap1, LOCATION, 1, 3L);
  8388.     if (!var) return NULLOBJ;
  8389.     Array2CArray(loc1, var);
  8390.  
  8391.     /*Get the rotation of the first snapshot*/
  8392.     var = GetFixedArrayVar("MakePlacmentJoints", snap1, ROTQUAT, 1, 4L);
  8393.     if (!var) return NULLOBJ;
  8394.     Array2CArray(q1, var);
  8395.  
  8396.     for (k = 0; k < nJoints; ++k)
  8397.     {
  8398.     real offset[3];            /*Offset from pivot*/
  8399.     real vl[3];            /*Component of velocity from local motion*/
  8400.     real vlt[3];            /*Transformed local velocity*/
  8401.     real vp[3];            /*Component of velocity from pivoting*/
  8402.     real vt[3];            /*Total velocity*/
  8403.     Quaternion qInv;        /*Inverse quaternion*/
  8404.     real radial[3];            /*Radial unit vector*/
  8405.     real direction[3];        /*Direction of movement due to rotation*/
  8406.  
  8407.     /*Get the next snapshot*/
  8408.     snap2 = GetObjectVar("MakePlacementJoints", keyframes[k + 1], SNAPSHOT);
  8409.     if (!snap2) return NULLOBJ;
  8410.  
  8411.     /*Get the location of the next snapshot*/
  8412.     var = GetFixedArrayVar("MakePlacementJoints", snap2, LOCATION, 1, 3L);
  8413.     if (!var) return NULLOBJ;
  8414.     Array2CArray(loc2, var);
  8415.  
  8416.     /*Get the rotation of the next snapshot*/
  8417.     var = GetFixedArrayVar("MakePlacementJoints", snap2, ROTQUAT, 1, 4L);
  8418.     if (!var) return NULLOBJ;
  8419.     Array2CArray(q2, var);
  8420.  
  8421.     /*Get the pivot of this joint*/
  8422.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], PIVOT, 1, 3L);
  8423.     if (!var) return NULLOBJ;
  8424.     Array2CArray(pivot, var);
  8425.  
  8426.     /*Calculate the local location for endpoint 1*/
  8427.     QInvert(q1, qInv);
  8428.     offset[0] = loc1[0] - pivot[0];
  8429.     offset[1] = loc1[1] - pivot[1];
  8430.     offset[2] = loc1[2] - pivot[2];
  8431.     QRot(offset, qInv, lloc1);
  8432.     var = NewRealArray(1, 3L);
  8433.     CArray2Array(var, lloc1);
  8434.     SetVar(joints[k], LLOCATION1, var);
  8435.  
  8436.     /*Calculate the local location for endpoint 2*/
  8437.     QInvert(q2, qInv);
  8438.     offset[0] = loc2[0] - pivot[0];
  8439.     offset[1] = loc2[1] - pivot[1];
  8440.     offset[2] = loc2[2] - pivot[2];
  8441.     QRot(offset, qInv, lloc2);
  8442.     var = NewRealArray(1, 3L);
  8443.     CArray2Array(var, lloc2);
  8444.     SetVar(joints[k], LLOCATION2, var);
  8445.  
  8446.     /*Get the delta time for this joint*/
  8447.     var = GetRealVar("MakePlacementJoints", joints[k], DELTATIME);
  8448.     if (!var) return NULLOBJ;
  8449.     deltaTime = GetReal(var);
  8450.  
  8451.     /*Calculate the velocity component due to motion within local coordinates*/
  8452.     vl[0] = (lloc2[0] - lloc1[0]) / deltaTime;
  8453.     vl[1] = (lloc2[1] - lloc1[1]) / deltaTime;
  8454.     vl[2] = (lloc2[2] - lloc1[2]) / deltaTime;
  8455.     
  8456.     /*Calculate the velocity component due to rotation at endpoint 1*/
  8457.  
  8458.     /*Get rotational speed*/
  8459.     var = GetRealVar("MakePlacementJoints", keyframes[k], ROTSPEED);
  8460.     if (!var) return NULLOBJ;
  8461.     rotSpeed = GetReal(var);
  8462.  
  8463.     /*Calculate the radius*/
  8464.     radius = rsqrt(SQUARE(lloc1[0]) + SQUARE(lloc1[1]) + SQUARE(lloc1[2]));
  8465.  
  8466.     if (rotSpeed == 0.0 || radius <= 0.0)
  8467.     {
  8468.         /*No contributed velocity due to rotation*/
  8469.        vp[0] = vp[1] = vp[2] = 0.0;
  8470.     }
  8471.     else
  8472.     {
  8473.         /*Calculate the speed*/
  8474.         speed = radius * rotSpeed;
  8475.  
  8476.         /*Get the rotational axis*/
  8477.         var = GetFixedArrayVar("MakePlacementJoints", keyframes[k], ROTAXIS, 1, 3L);
  8478.         if (!var) return NULLOBJ;
  8479.         Array2CArray(axis, var);
  8480.  
  8481.         /*Calculate the radial and movement direction*/
  8482.         radial[0] = loc1[0] - pivot[0];
  8483.         radial[1] = loc1[1] - pivot[1];
  8484.         radial[2] = loc1[2] - pivot[2];
  8485.         NORMALIZE(radial);
  8486.         CROSS(radial, axis, direction);
  8487.         /*Direction is not normalized, so it compensates for skew radial*/
  8488.  
  8489.         /*Now calculate the component*/
  8490.         vp[0] = direction[0] * speed;
  8491.         vp[1] = direction[1] * speed;
  8492.         vp[2] = direction[2] * speed;
  8493.     }
  8494.  
  8495.     /*Rotate the linear velocity to get it in world coordinates*/
  8496.     QRot(vl, q1, vlt);
  8497.  
  8498.     /*Make total velocity*/
  8499.     vt[0] = vp[0] + vlt[0]; 
  8500.     vt[1] = vp[1] + vlt[1]; 
  8501.     vt[2] = vp[2] + vlt[2];
  8502.  
  8503.     /*Make it the desired velocity*/
  8504.     var = NewRealArray(1, 3L);
  8505.     CArray2Array(var, vt);
  8506.     SetVar(joints[k], DESVEL1, var);
  8507.  
  8508.     /*Calculate the velocity component due to rotation at endpoint 2*/
  8509.  
  8510.     /*Get rotational speed*/
  8511.     var = GetRealVar("MakePlacementJoints", keyframes[k], ROTSPEED);
  8512.     if (!var) return NULLOBJ;
  8513.     rotSpeed = GetReal(var);
  8514.  
  8515.     /*Calculate the radius*/
  8516.     radius = rsqrt(SQUARE(lloc2[0]) + SQUARE(lloc2[1]) + SQUARE(lloc2[2]));
  8517.  
  8518.     if (rotSpeed == 0.0 || radius <= 0.0)
  8519.     {
  8520.         /*No contributed velocity due to rotation*/
  8521.        vp[0] = vp[1] = vp[2] = 0.0;
  8522.     }
  8523.     else
  8524.     {
  8525.         /*Calculate the speed*/
  8526.         speed = radius * rotSpeed;
  8527.  
  8528.         /*Get the rotational axis*/
  8529.         var = GetFixedArrayVar("MakePlacementJoints", keyframes[k], ROTAXIS, 1, 3L);
  8530.         if (!var) return NULLOBJ;
  8531.         Array2CArray(axis, var);
  8532.  
  8533.         /*Calculate the radial and movement direction*/
  8534.         radial[0] = loc2[0] - pivot[0];
  8535.         radial[1] = loc2[1] - pivot[1];
  8536.         radial[2] = loc2[2] - pivot[2];
  8537.         NORMALIZE(radial);
  8538.         CROSS(radial, axis, direction);
  8539.  
  8540.         /*Now calculate the component*/
  8541.         vp[0] = direction[0] * speed;
  8542.         vp[1] = direction[1] * speed;
  8543.         vp[2] = direction[2] * speed;
  8544.     }
  8545.  
  8546.     /*Rotate the linear velocity to get it in world coordinates*/
  8547.     QRot(vl, q2, vlt);
  8548.  
  8549.     /*Make total velocity*/
  8550.     vt[0] = vp[0] + vlt[0]; 
  8551.     vt[1] = vp[1] + vlt[1]; 
  8552.     vt[2] = vp[2] + vlt[2];
  8553.  
  8554.     /*Make it the desired velocity*/
  8555.     var = NewRealArray(1, 3L);
  8556.     CArray2Array(var, vt);
  8557.     SetVar(joints[k], DESVEL2, var);
  8558.  
  8559.     /*Shift the variables down for the next step*/
  8560.     snap1 = snap2;
  8561.     loc1[0] = loc2[0];
  8562.     loc1[1] = loc2[1];
  8563.     loc1[2] = loc2[2];
  8564.     q1[0] = q2[0];
  8565.     q1[1] = q2[1];
  8566.     q1[2] = q2[2];
  8567.     q1[3] = q2[3];
  8568.     }
  8569.  
  8570.     /*Use the desired velocities to make consensus velocities for all keyframes*/
  8571.     SetVar(keyframes[0], WORLDVELOCITY, GetVar(joints[0], DESVEL1));
  8572.     SetVar(keyframes[nKeyframes - 1], WORLDVELOCITY, GetVar(joints[nJoints - 1], DESVEL2));
  8573.  
  8574.     var = GetRealVar("MakePlacementJoints", joints[0], WEIGHT);
  8575.     if (!var) return NULLOBJ;
  8576.     w1 = GetReal(var);
  8577.  
  8578.     for (k = 1; k < nKeyframes - 1; ++k)
  8579.     {
  8580.     real desVel1[3], desVel2[3];    /*Desired velocities*/
  8581.     real wv[3];            /*Final world velocity*/
  8582.  
  8583.     var = GetRealVar("MakePlacementJoints", joints[k], WEIGHT);
  8584.     if (!var) return NULLOBJ;
  8585.     w2 = GetReal(var);
  8586.  
  8587.     if (w1 + w2 <= 0.0)
  8588.     {
  8589.         /*Neither has weight, so weigh evenly*/
  8590.     }
  8591.  
  8592.     var = GetFixedArrayVar("MakePlacementJoints", joints[k - 1], DESVEL2, 1, 3L);
  8593.     if (!var) return NULLOBJ;
  8594.     Array2CArray(desVel1, var);
  8595.  
  8596.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], DESVEL1, 1, 3L);
  8597.     if (!var) return NULLOBJ;
  8598.     Array2CArray(desVel2, var);
  8599.  
  8600.     wv[0] = (desVel1[0] * w1 + desVel2[0] * w2) / (w1 + w2);
  8601.     wv[1] = (desVel1[1] * w1 + desVel2[1] * w2) / (w1 + w2);
  8602.     wv[2] = (desVel1[2] * w1 + desVel2[2] * w2) / (w1 + w2);
  8603.  
  8604.     var = NewRealArray(1, 3L);
  8605.     CArray2Array(var, wv);
  8606.     SetVar(keyframes[k], WORLDVELOCITY, var);
  8607.  
  8608.     w1 = w2;
  8609.     }
  8610.  
  8611.     /*Print out the world velocities*/
  8612.     for (k = 0; k < nKeyframes; ++k)
  8613.     {
  8614.     real wv[3];
  8615.     printf("keyframe %d\n", k);
  8616.     var = GetVar(keyframes[k], WORLDVELOCITY);
  8617.     Array2CArray(wv, var);
  8618.     printf("    world velocity = %g %g %g\n", wv[0], wv[1], wv[2]);
  8619.     }
  8620.  
  8621.     /*Use the consensus world velocity and rotational velocities to work 
  8622.     backward to get individual endpoint local linear velocities*/
  8623.  
  8624.     snap1 = GetObjectVar("MakePlacementJoints", keyframes[0], SNAPSHOT);
  8625.     if (!snap1) return NULLOBJ;
  8626.  
  8627.     var = GetFixedArrayVar("MakePlacementJoints", snap1, LOCATION, 1, 3L);
  8628.     if (!var) return NULLOBJ;
  8629.     Array2CArray(loc1, var);
  8630.  
  8631.     var = GetFixedArrayVar("MakePlacementJoints", snap1, ROTQUAT, 1, 4L);
  8632.     if (!var) return NULLOBJ;
  8633.     Array2CArray(q1, var);
  8634.  
  8635.     for (k = 0; k < nJoints; ++k)
  8636.     {
  8637.     real vp[3];        /*Component due to pivot*/
  8638.     real direction[3];    /*Direction of pivot motion*/
  8639.     real radial[3];        /*Radial vector*/
  8640.     real vl[3];        /*Local velocity*/
  8641.     real vlt[3];        /*Local velocity transformed*/
  8642.     real wv[3];        /*World velocity*/
  8643.     Quaternion qInv;    /*Inverse quaternion*/
  8644.  
  8645.     /*Get pivot*/
  8646.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], PIVOT, 1, 3L);
  8647.     if (!var) return NULLOBJ;
  8648.     Array2CArray(pivot, var);
  8649.  
  8650.     /*Do endpoint1*/
  8651.  
  8652.     /*Get axis and speed of endpoint 1*/
  8653.     var = GetFixedArrayVar("MakePlacementJoints", keyframes[k], ROTAXIS, 1, 3L);
  8654.     if (!var) return NULLOBJ;
  8655.     Array2CArray(axis, var);
  8656.     var = GetRealVar("MakePlacementJoints", keyframes[k], ROTSPEED);
  8657.     if (!var) return NULLOBJ;
  8658.     rotSpeed = GetReal(var);
  8659.  
  8660.     /*Get local location*/
  8661.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], LLOCATION1, 1, 3L);
  8662.     if (!var) return NULLOBJ;
  8663.     Array2CArray(lloc1, var);
  8664.  
  8665.     /*Calculate the radius*/
  8666.     radius = sqrt(SQUARE(lloc1[0]) + SQUARE(lloc1[1]) + SQUARE(lloc1[2]));
  8667.  
  8668.     if (radius <= 0.0 || rotSpeed == 0.0)
  8669.     {
  8670.         /*No rotational component*/
  8671.         vp[0] = vp[1] = vp[2] = 0.0;
  8672.     }
  8673.     else
  8674.     {
  8675.         /*Now the total speed, not compensated by skew radial*/
  8676.         speed = radius * rotSpeed;
  8677.  
  8678.         /*Calculate the radial and movement direction*/
  8679.         radial[0] = loc1[0] - pivot[0];
  8680.         radial[1] = loc1[1] - pivot[1];
  8681.         radial[2] = loc1[2] - pivot[2];
  8682.         NORMALIZE(radial);
  8683.         CROSS(radial, axis, direction);
  8684.         /*Direction is not normalized, so it compensates for skew radial*/
  8685.  
  8686.         /*Now calculate the component*/
  8687.         vp[0] = direction[0] * speed;
  8688.         vp[1] = direction[1] * speed;
  8689.         vp[2] = direction[2] * speed;
  8690.     }
  8691.  
  8692.     /*Get world velocity*/
  8693.     var = GetFixedArrayVar("MakePlacementJoints", keyframes[k], WORLDVELOCITY, 1, 3L);
  8694.     if (!var) return NULLOBJ;
  8695.     Array2CArray(wv, var);
  8696.  
  8697.     /*Get linear component of velocity*/
  8698.     vlt[0] = wv[0] - vp[0];
  8699.     vlt[1] = wv[1] - vp[1];
  8700.     vlt[2] = wv[2] - vp[2];
  8701.  
  8702.     /*Rotate back to local coordinates*/
  8703.     QInvert(q1, qInv);
  8704.     QRot(vlt, qInv, vl);
  8705.  
  8706.     /*Put vl back as local velocity*/
  8707.     var = NewRealArray(1, 3L);
  8708.     CArray2Array(var, vl);
  8709.     SetVar(joints[k], LVELOCITY1, var);
  8710.  
  8711.     /*Go to next endpoint*/
  8712.  
  8713.     snap2 = GetObjectVar("MakePlacementJoints", keyframes[k + 1], SNAPSHOT);
  8714.     if (!snap2) return NULLOBJ;
  8715.  
  8716.     var = GetFixedArrayVar("MakePlacementJoints", snap2, LOCATION, 1, 3L);
  8717.     if (!var) return NULLOBJ;
  8718.     Array2CArray(loc2, var);
  8719.  
  8720.     var = GetFixedArrayVar("MakePlacementJoints", snap2, ROTQUAT, 1, 4L);
  8721.     if (!var) return NULLOBJ;
  8722.     Array2CArray(q2, var);
  8723.  
  8724.     /*Do endpoint 2*/
  8725.  
  8726.     /*Get axis and speed of endpoint 2*/
  8727.     var = GetFixedArrayVar("MakePlacementJoints", keyframes[k + 1], ROTAXIS, 1, 3L);
  8728.     if (!var) return NULLOBJ;
  8729.     Array2CArray(axis, var);
  8730.     var = GetRealVar("MakePlacementJoints", keyframes[k + 1], ROTSPEED);
  8731.     if (!var) return NULLOBJ;
  8732.     rotSpeed = GetReal(var);
  8733.  
  8734.     /*Get local location*/
  8735.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], LLOCATION2, 1, 3L);
  8736.     if (!var) return NULLOBJ;
  8737.     Array2CArray(lloc2, var);
  8738.  
  8739.     /*Calculate the radius*/
  8740.     radius = sqrt(SQUARE(lloc2[0]) + SQUARE(lloc2[1]) + SQUARE(lloc2[2]));
  8741.  
  8742.     if (radius <= 0.0 || rotSpeed == 0.0)
  8743.     {
  8744.         /*No rotational component*/
  8745.         vp[0] = vp[1] = vp[2] = 0.0;
  8746.     }
  8747.     else
  8748.     {
  8749.         /*Now the total speed, not compensated by skew radial*/
  8750.         speed = radius * rotSpeed;
  8751.  
  8752.         /*Calculate the radial and movement direction*/
  8753.         radial[0] = loc2[0] - pivot[0];
  8754.         radial[1] = loc2[1] - pivot[1];
  8755.         radial[2] = loc2[2] - pivot[2];
  8756.         NORMALIZE(radial);
  8757.         CROSS(radial, axis, direction);
  8758.         /*Direction is not normalized, so it compensates for skew radial*/
  8759.  
  8760.         /*Now calculate the component*/
  8761.         vp[0] = direction[0] * speed;
  8762.         vp[1] = direction[1] * speed;
  8763.         vp[2] = direction[2] * speed;
  8764.     }
  8765.  
  8766.     /*Get world velocity*/
  8767.     var = GetFixedArrayVar("MakePlacementJoints", keyframes[k + 1], WORLDVELOCITY, 1, 3L);
  8768.     if (!var) return NULLOBJ;
  8769.     Array2CArray(wv, var);
  8770.  
  8771.     /*Get linear component of velocity*/
  8772.     vlt[0] = wv[0] - vp[0];
  8773.     vlt[1] = wv[1] - vp[1];
  8774.     vlt[2] = wv[2] - vp[2];
  8775.  
  8776.     /*Rotate back to local coordinates*/
  8777.     QInvert(q2, qInv);
  8778.     QRot(vlt, qInv, vl);
  8779.  
  8780.     /*Put vl back as local velocity*/
  8781.     var = NewRealArray(1, 3L);
  8782.     CArray2Array(var, vl);
  8783.     SetVar(joints[k], LVELOCITY2, var);
  8784.  
  8785.     /*Shift variables for next iteration*/
  8786.     snap1 = snap2;
  8787.     loc1[0] = loc2[0];
  8788.     loc1[1] = loc2[1];
  8789.     loc1[2] = loc2[2];
  8790.     q1[0] = q2[0];
  8791.     q1[1] = q2[1];
  8792.     q1[2] = q2[2];
  8793.     q1[3] = q2[3];
  8794.     }
  8795.  
  8796.     /*Print out local velocities*/
  8797.     for (k = 0; k < nJoints; ++k)
  8798.     {
  8799.     real vl[3];
  8800.     printf("joint %d:\n", k);
  8801.     var = GetVar(joints[k], LVELOCITY1);
  8802.     Array2CArray(vl, var);
  8803.     printf("    vel1 = %g %g %g\n", vl[0], vl[1], vl[2]);
  8804.     var = GetVar(joints[k], LVELOCITY2);
  8805.     Array2CArray(vl, var);
  8806.     printf("    vel2 = %g %g %g\n", vl[0], vl[1], vl[2]);
  8807.     }
  8808.  
  8809.     /*Go through and make A and B points for Bezier rotations*/
  8810.     for (k = 0; k < nKeyframes; ++k)
  8811.     {
  8812.     Quaternion qA, qB, qInv;
  8813.     real tempRotation;
  8814.     int thisFrame;
  8815.     real dt;
  8816.  
  8817.     snap1 = GetObjectVar("MakePlacementJoints", keyframes[k], SNAPSHOT);
  8818.     if (!snap1) return NULLOBJ;
  8819.  
  8820.     /*Get axis of rotation*/
  8821.     var = GetFixedArrayVar("MakePlacementJoints", keyframes[k], ROTAXIS, 1, 3L);
  8822.     if (!var) return NULLOBJ;
  8823.     Array2CArray(axis, var);
  8824.  
  8825.     /*Get speed of rotation in frame units*/
  8826.     var = GetRealVar("MakePlacementJoints", keyframes[k], ROTSPEED);
  8827.     if (!var) return NULLOBJ;
  8828.     rotSpeed = GetReal(var);
  8829.  
  8830.     /*Get quaternion at this keyframe*/
  8831.     var = GetFixedArrayVar("MakePlacementJoints", snap1, ROTQUAT, 1, 4L);
  8832.     if (!var) return NULLOBJ;
  8833.     Array2CArray(q1, var);
  8834.  
  8835.     /*Get frame number at this keyframe*/
  8836.  
  8837.     if (k < nKeyframes - 1)
  8838.     {
  8839.         /*There's an A.*/
  8840.  
  8841.         /*Get delta time to next joint*/
  8842.         var = GetRealVar("MakePlacementJoints", joints[k], DELTATIME);
  8843.         if (!var) return NULLOBJ;
  8844.         dt = GetReal(var);
  8845.  
  8846.         /*Make and adjusted rotation from speed and delta time,
  8847.           divided by 3 for Bezier stuff*/
  8848.  
  8849.         tempRotation = rotSpeed * dt / 3.0;
  8850.         RotToQuaternion(axis, tempRotation, q2);
  8851.  
  8852.         /*Produce A quaternion*/
  8853.         QMult(q1, q2, qr);
  8854.         var = NewRealArray(1, 4L);
  8855.         CArray2Array(var, qr);
  8856.         SetVar(keyframes[k], ROTA, var);
  8857.     }
  8858.     if (k > 0)
  8859.     {
  8860.         /*There's a B.*/
  8861.  
  8862.         /*Get delta time to next joint*/
  8863.         var = GetRealVar("MakePlacementJoints", joints[k - 1], DELTATIME);
  8864.         if (!var) return NULLOBJ;
  8865.         dt = GetReal(var);
  8866.  
  8867.         /*Make and adjusted rotation from speed and delta time,
  8868.           divided by 3 for Bezier stuff*/
  8869.  
  8870.         tempRotation = -rotSpeed * dt / 3.0;
  8871.         RotToQuaternion(axis, tempRotation, q2);
  8872.  
  8873.         /*Produce A quaternion*/
  8874.         QMult(q1, q2, qr);
  8875.         var = NewRealArray(1, 4L);
  8876.         CArray2Array(var, qr);
  8877.         SetVar(keyframes[k], ROTB, var);
  8878.     }
  8879.     }
  8880.  
  8881.     /*Now go through and make A and B in local coords for joint endpoints*/
  8882.     for (k = 0; k < nJoints; ++k)
  8883.     {
  8884.     real lv1[3], lv2[3];        /*Local velocities*/
  8885.     real cp[3];            /*Control point*/
  8886.     real dt;            /*Delta time*/
  8887.  
  8888.     /*Get delta time*/
  8889.     var = GetRealVar("MakePlacementJoints", joints[k], DELTATIME);
  8890.     if (!var) return NULLOBJ;
  8891.     dt = GetReal(var);
  8892.  
  8893.     /*Get local locations*/
  8894.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], LLOCATION1, 1, 3L);
  8895.     if (!var) return NULLOBJ;
  8896.     Array2CArray(lloc1, var);
  8897.  
  8898.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], LLOCATION2, 1, 3L);
  8899.     if (!var) return NULLOBJ;
  8900.     Array2CArray(lloc2, var);
  8901.  
  8902.     /*Get local velocities*/
  8903.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], LVELOCITY1, 1, 3L);
  8904.     if (!var) return NULLOBJ;
  8905.     Array2CArray(lv1, var);
  8906.  
  8907.     var = GetFixedArrayVar("MakePlacementJoints", joints[k], LVELOCITY2, 1, 3L);
  8908.     if (!var) return NULLOBJ;
  8909.     Array2CArray(lv2, var);
  8910.  
  8911.     /*Make A for endpoint 1*/
  8912.     cp[0] = lloc1[0] + lv1[0] * dt / 3.0;
  8913.     cp[1] = lloc1[1] + lv1[1] * dt / 3.0;
  8914.     cp[2] = lloc1[2] + lv1[2] * dt / 3.0;
  8915.     var = NewRealArray(1, 3L);
  8916.     CArray2Array(var, cp);
  8917.     SetVar(joints[k], LOCA, var);
  8918.  
  8919.     /*Make B for endpoint 2*/
  8920.     cp[0] = lloc2[0] - lv2[0] * dt / 3.0;
  8921.     cp[1] = lloc2[1] - lv2[1] * dt / 3.0;
  8922.     cp[2] = lloc2[2] - lv2[2] * dt / 3.0;
  8923.     var = NewRealArray(1, 3L);
  8924.     CArray2Array(var, cp);
  8925.     SetVar(joints[k], LOCB, var);
  8926.  
  8927.     }
  8928.  
  8929.     return ObjTrue;
  8930. }
  8931.  
  8932. #ifdef PROTO
  8933. Bool TweenObserverPlacement(ObjPtr track, int whichFrame, Quaternion qr, real locr[3])
  8934. #else
  8935. Bool TweenObserverPlacement(track, whichFrame, qr, locr)
  8936. ObjPtr track;
  8937. int whichFrame;
  8938. Quaternion qr;
  8939. real locr[3];
  8940. #endif
  8941. /*Finds a tween for the observer on track at whichFrame, returning the value
  8942.   in qr and locr*/
  8943. {
  8944.     int k;            /*Index to which joint this is*/
  8945.     ObjPtr var;            /*Random variable*/
  8946.     ObjPtr *keyframes, *joints;    /*Keyframes and joints*/
  8947.     ObjPtr snap1, snap2;    /*Snapshots*/
  8948.     real s;            /*Parameter into quaternion*/
  8949.     int frame1;            /*WHICHFRAME of the two frames*/
  8950.     int frame2;
  8951.     Quaternion q1, q2;        /*First and second quaternions*/
  8952.     Quaternion qa, qb;        /*A and B quaternions*/
  8953.     Quaternion qh0, qh1, qh2;    /*Bezier interpolation quaternions*/
  8954.     real l1[3], l2[3];        /*First and second locations*/
  8955.     real la[3], lb[3];        /*A and B locations*/
  8956.     real lh0[3], lh1[3], lh2[3];/*Bezier interpolation locations*/
  8957.     real lr[3];            /*Local result location*/
  8958.     real offset[3];        /*Offset from pivot to result*/
  8959.     real pivot[3];        /*Pivot*/
  8960.  
  8961.     /*Get the keyframes*/
  8962.     var = GetVar(track, KEYFRAMES);
  8963.     if (!var) return false;
  8964.     keyframes = ELEMENTS(var);
  8965.  
  8966.     /*Find which joint to use*/
  8967.     k = SearchIntVar(var, WHICHFRAME, whichFrame);
  8968.     if (k > 0) --k;
  8969.     if (k >= DIMS(var)[0] - 1) --k;
  8970.     if (k < 0) return false;
  8971.  
  8972.     /*Get the joints*/
  8973.     var = GetVar(track, JOINTS);
  8974.     if (!var) return false;
  8975.     joints = ELEMENTS(var);
  8976.  
  8977.     var = GetIntVar("TweenObserverPlacement", keyframes[k], WHICHFRAME);
  8978.     if (!var) return false;
  8979.     frame1 = GetInt(var);
  8980.  
  8981.     var = GetIntVar("TweenObserverPlacement", keyframes[k + 1], WHICHFRAME);
  8982.     if (!var) return false;
  8983.     frame2 = GetInt(var);
  8984.  
  8985.     /*Make the parameter in the range [0...1]*/
  8986.     s = ((real) (whichFrame - frame1)) / ((real) (frame2 - frame1));
  8987.  
  8988.     /*Get the snapshots*/
  8989.     snap1 = GetObjectVar("TweenObserverPlacement", keyframes[k], SNAPSHOT);
  8990.     if (!snap1) return false;
  8991.     snap2 = GetObjectVar("TweenObserverPlacement", keyframes[k + 1], SNAPSHOT);
  8992.     if (!snap2) return false;
  8993.  
  8994.     /*Interpolate quaternions*/
  8995.  
  8996.     /*Get the source quaternions*/
  8997.     var = GetFixedArrayVar("TweenObserverPlacement", snap1, ROTQUAT, 1, 4L);
  8998.     if (!var) return false;
  8999.     Array2CArray(q1, var);
  9000.  
  9001.     var = GetFixedArrayVar("TweenObserverPlacement", snap2, ROTQUAT, 1, 4L);
  9002.     if (!var) return false;
  9003.     Array2CArray(q2, var);
  9004.  
  9005.     /*Get the A and B quaternions*/
  9006.     var = GetFixedArrayVar("TweenObserverPlacement", keyframes[k], ROTA, 1, 4L);
  9007.     if (!var) return false;
  9008.     Array2CArray(qa, var);
  9009.  
  9010.     var = GetFixedArrayVar("TweenObserverPlacement", keyframes[k + 1], ROTB, 1, 4L);
  9011.     if (!var) return false;
  9012.     Array2CArray(qb, var);
  9013.  
  9014.     /*Calculate 3 helper quaternions from q1, qa, qb, and q2*/
  9015.     Slerp(q1, qa, s, qh0);
  9016.     Slerp(qa, qb, s, qh1);
  9017.     Slerp(qb, q2, s, qh2);
  9018.  
  9019.     /*Interpolate qh0, qh1, and qh2 to get qh0 and qh1*/
  9020.     Slerp(qh0, qh1, s, qh0);
  9021.     Slerp(qh1, qh2, s, qh1);
  9022.  
  9023.     /*Finally, interpolate qh0 and qh1 to get the result*/
  9024.     Slerp(qh0, qh1, s, qr);
  9025.  
  9026.     /*Interpolate locations*/
  9027.  
  9028.     /*Get the source locations*/
  9029.     var = GetFixedArrayVar("TweenObserverPlacement", joints[k], LLOCATION1, 1, 3L);
  9030.     if (!var) return false;
  9031.     Array2CArray(l1, var);
  9032.  
  9033.     var = GetFixedArrayVar("TweenObserverPlacement", joints[k], LLOCATION2, 1, 3L);
  9034.     if (!var) return false;
  9035.     Array2CArray(l2, var); 
  9036.  
  9037.     /*Get the A and B locations*/
  9038.     var = GetFixedArrayVar("TweenObserverPlacement", joints[k], LOCA, 1, 3L);
  9039.     if (!var) return false;
  9040.     Array2CArray(la, var);
  9041.  
  9042.     var = GetFixedArrayVar("TweenObserverPlacement", joints[k], LOCB, 1, 3L);
  9043.     if (!var) return false;
  9044.     Array2CArray(lb, var);
  9045.  
  9046.     /*Calculate 3 helper locations from l1, la, lb, and l2*/
  9047.     INT3(l1, la, s, lh0);
  9048.     INT3(la, lb, s, lh1);
  9049.     INT3(lb, l2, s, lh2);
  9050.  
  9051.     /*Interpolate lh0, lh1, and lh2 to get lh0 and lh1*/
  9052.     INT3(lh0, lh1, s, lh0);
  9053.     INT3(lh1, lh2, s, lh1);
  9054.  
  9055.     /*Finally, interpolate lh0 and lh1 to get result*/
  9056.     INT3(lh0, lh1, s, lr);
  9057.  
  9058.     /*Rotate local position by quaternion to get offset*/
  9059.     QRot(lr, qr, offset);
  9060.  
  9061.     /*Offset pivot to get result*/
  9062.     var = GetFixedArrayVar("TweenObserverPlacement", joints[k], PIVOT, 1, 3L);
  9063.     if (!var) return false;
  9064.     Array2CArray(pivot, var);
  9065.  
  9066.     locr[0] = pivot[0] + offset[0];
  9067.     locr[1] = pivot[1] + offset[1];
  9068.     locr[2] = pivot[2] + offset[2];
  9069.  
  9070.     return true;
  9071. }
  9072.  
  9073. void InitSpaces()
  9074. /*Initializes the spaces*/
  9075. {
  9076.     ObjPtr icon;
  9077.     ObjPtr list;
  9078.     ObjPtr var;
  9079.     real curViewClip[2];
  9080.  
  9081.     /*Create a class of spaces*/
  9082.     spaceClass = NewObject(NULLOBJ, 0);
  9083.     AddToReferenceList(spaceClass);
  9084.     SetVar(spaceClass, CLASSID, NewInt(CLASS_SPACE));
  9085.     SetMethod(spaceClass, DRAW, DrawSpace);
  9086.     SetVar(spaceClass, NAME, NewString("Space"));
  9087.     SetMethod(spaceClass, PRESS, PressSpace);
  9088.     SetMethod(spaceClass, TURNDIAL, TurnDialSpace);
  9089.     SetMethod(spaceClass, KEYDOWN, KeyDownSpace);
  9090.     SetMethod(spaceClass, BOUNDSINVALID, SpaceBoundsInvalid);
  9091.     SetVar(spaceClass, TYPESTRING, NewString("space"));
  9092.     SetVar(spaceClass, HELPSTRING,
  9093.     NewString("A space provides a 3-dimensional world for visualization \
  9094. objects.  Objects are automatically scaled and translated to appear near the \
  9095. center by default.  Any number of visualization objects can exist within a space. \
  9096. Spaces are controlled by controllers, such as observers and lights.\n\
  9097. \n\
  9098. The tools control panel on the left controls how the mouse affects the space.  \
  9099. For more information on a particular tool, use help in context on the tool button."));
  9100.  
  9101. #ifdef GRAPHICS
  9102.     lmdef(DEFMATERIAL, 1, 0, NULL);
  9103. #endif
  9104.  
  9105.     SetMethod(spaceClass, FORALLOBJECTS, ForAllSpaceObjects); 
  9106.     SetMethod(spaceClass, NEWCTLWINDOW, ShowSpaceControls);
  9107.     SetMethod(spaceClass, SHOWCONTROLS, NewControlWindow);
  9108.     DeclareDependency(spaceClass, TIME, CLOCK);
  9109.     DeclareIndirectDependency(spaceClass, TIME, CLOCK, TIME);
  9110.     SetMethod(spaceClass, TIME, MakeSpaceTime);
  9111.     DeclareDependency(spaceClass, APPEARANCE, TIME);
  9112.     SetMethod(spaceClass, APPEARANCE, MakeSpaceAppearance);
  9113.  
  9114.     DeclareDependency(spaceClass, APPEARANCE, RENDERER);
  9115.  
  9116.     DeclareDependency(spaceClass, APPEARANCE, RENDERTYPE);
  9117.     DeclareDependency(spaceClass, RENDERTYPE, RENDERER);
  9118.     DeclareIndirectDependency(spaceClass, RENDERTYPE, RENDERER, RENDERTYPE);
  9119.     SetMethod(spaceClass, RENDERTYPE, MakeSpaceRenderType);
  9120.  
  9121.     DeclareDependency(spaceClass, APPEARANCE, FILTERTYPE);
  9122.     DeclareDependency(spaceClass, FILTERTYPE, RENDERER);
  9123.     DeclareIndirectDependency(spaceClass, FILTERTYPE, RENDERER, FILTERTYPE);
  9124.     SetMethod(spaceClass, FILTERTYPE, MakeSpaceFilterType);
  9125.  
  9126.     /*Initialize a space panel*/
  9127.     spacePanelClass = NewObject(panelClass, 0);
  9128.     AddToReferenceList(spacePanelClass);
  9129.     SetMethod(spacePanelClass, DROPOBJECTS, DropInSpacePanel);
  9130.     SetMethod(spacePanelClass, RESHAPE, ReshapeSpacePanel);
  9131.     SetMethod(spacePanelClass, DRAW, DrawSpacePanel);
  9132.     SetMethod(spacePanelClass, BOUNDSINVALID, SpacePanelBoundsInvalid);
  9133.  
  9134.     /*Initialize a space back panel*/
  9135.     spaceBackPanelClass = NewObject(spacePanelClass, 0);
  9136.     AddToReferenceList(spaceBackPanelClass);
  9137.     SetVar(spaceBackPanelClass, BACKGROUND, NewInt(UIBLACK));
  9138.  
  9139.     /*Create class of space controllers*/
  9140.     controllerClass = NewObject(NULLOBJ, 0);
  9141.     AddToReferenceList(controllerClass);
  9142.     SetVar(controllerClass, CONTROLLERP, ObjTrue);
  9143.     SetMethod(controllerClass, SHOWCONTROLS, NewControlWindow);
  9144.  
  9145.     /*Create class of clocks*/
  9146.     clockClass = NewObject(controllerClass, 0);
  9147.     AddToReferenceList(clockClass);
  9148.     SetMethod(clockClass, MARKTIME, MarkClockTime);
  9149.     SetMethod(clockClass, CLONE, CloneClock);
  9150.     SetMethod(clockClass, BINDTOSPACE, BindClockToSpace);
  9151.     SetMethod(clockClass, TIMEBOUNDS, MakeClockTimeBounds);
  9152.     SetMethod(clockClass, NEWCTLWINDOW, ShowClockControls);
  9153.     SetVar(clockClass, DOUBLECLICK, NewString(OF_SHOW_CONTROLS));
  9154.     SetMethod(clockClass, REINIT, MakeClockDatasets);
  9155.     SetVar(clockClass, CLASSID, NewInt(CLASS_CLOCK));
  9156.     DeclareDependency(clockClass, TIMESTEPS, DATASETS);
  9157.     SetVar(clockClass, DTIMEPER, NewReal(1));
  9158.     SetMethod(clockClass, TIMESTEPS, MakeClockTimeSteps);
  9159.     SetMethod(clockClass, DTIME, MakeClockDTime);
  9160.     SetVar(clockClass, ONEONLY, ObjTrue);
  9161.     SetVar(clockClass, DEFAULTICON, icon = NewIcon(0, 0, ICONCLOCK, "Clock"));
  9162.     SetVar(icon, HELPSTRING,
  9163.     NewString("This icon represents a clock.  The clock controls the current \
  9164. time displayed within a space and the rate at which time goes forward or backward.  \
  9165. You can see controls for this clock by selecting it and choosing the Show Controls \
  9166. item in the Object menu.\n\
  9167. \n\
  9168. You can drag this icon into the icon corral of another visualization window to have \
  9169. it control the other space as well.  \
  9170. A space can only be controlled by one clock at a time.  If you drag another \
  9171. clock icon into this space, this clock will be replaced.\n\
  9172. \n\
  9173. You can place a time display in the image of the space itself by dragging this \
  9174. icon into the space itself."));
  9175.     SetVar(clockClass, NAME, NewString("Clock"));
  9176.     DeclareDependency(clockClass, TIMEFORMAT, DATASETS);
  9177.     SetMethod(clockClass, TIMEFORMAT, MakeClockTimeFormat);
  9178.  
  9179.     /*Create class of placements*/
  9180.     placementClass = NewObject(controllerClass, 0L);
  9181.     AddToReferenceList(placementClass);
  9182.     AddSnapVar(placementClass, LOCATION);
  9183.     AddSnapVar(placementClass, FORWARDVECTOR);
  9184.     AddSnapVar(placementClass, UPVECTOR);
  9185.     AddSnapVar(placementClass, FOCUSDIST);
  9186.     icon = NewIcon(0, 0, ICONPLACEMENT, "Placement");
  9187.     SetVar(placementClass, CONTROLICON, icon);
  9188.     DeclareDependency(placementClass, FORWARDVECTOR, ROTQUAT);
  9189.     SetMethod(placementClass, FORWARDVECTOR, MakeObserverForwardVector);
  9190.     DeclareDependency(placementClass, UPVECTOR, ROTQUAT);
  9191.     SetMethod(placementClass, ADDCONTROLS, AddPlacementControls);
  9192.     SetMethod(placementClass, UPVECTOR, MakeObserverUpVector);
  9193.     {
  9194.     /*Give it an initial identity rotation quaternion*/
  9195.     ObjPtr var;
  9196.     Quaternion rotQuat;
  9197.  
  9198.     rotQuat[0] = 1.0;
  9199.     rotQuat[1] = 0.0;
  9200.     rotQuat[2] = 0.0;
  9201.     rotQuat[3] = 0.0;
  9202.     var = NewRealArray(1, 4L);
  9203.     CArray2Array(var, rotQuat);
  9204.     SetVar(placementClass, ROTQUAT, var);
  9205.     }
  9206.  
  9207.     /*Create a class of observers with stereo*/
  9208.     stereoClass = NewObject(placementClass, 0L);
  9209.     AddToReferenceList(stereoClass);
  9210.     icon = NewIcon(0, 0, ICONGLASSES, "Stereo");
  9211.     SetVar(stereoClass, CONTROLICON, icon);
  9212.     SetMethod(stereoClass, ADDCONTROLS, AddStereoControls);
  9213.  
  9214.     /*Create class of observers*/
  9215.     observerClass = NewObject(stereoClass, 0L);
  9216.     AddToReferenceList(observerClass);
  9217.     icon = NewIcon(0, 0, ICONOBSERVER, "View");
  9218.     SetVar(observerClass, CONTROLICON, icon);
  9219.     SetMethod(observerClass, BINDTOSPACE, BindObserverToSpace);
  9220.     SetMethod(observerClass, NEWCTLWINDOW, ShowObserverControls);
  9221.     SetVar(observerClass, DOUBLECLICK, NewString(OF_SHOW_CONTROLS));
  9222.     SetMethod(observerClass, LOCALCOPY, MakeLocalCopy);
  9223.     SetMethod(observerClass, ADDCONTROLS, AddViewControls);
  9224.     SetVar(observerClass, VIEWTYPE, NewInt(VT_PERSPECTIVE));
  9225.     SetVar(observerClass, STEREOMODE, NewInt(SM_MONO));
  9226.     SetVar(observerClass, FOCUSDIST, NewReal(INITEYEDIST));
  9227.     SetVar(observerClass, VIEWFIELD, NewReal(INITAOV));
  9228.     SetMethod(observerClass, DRAW, DrawObserver);
  9229.     curViewClip[0] = INITNEARCLIP;
  9230.     curViewClip[1] = INITFARCLIP;
  9231.     var = NewRealArray(1, 2L);
  9232.     CArray2Array(var, curViewClip);
  9233.     SetVar(observerClass, VIEWCLIP, var);
  9234.     {
  9235.     ObjPtr clipArray;
  9236.     real clip[2];
  9237.     clip[0] = INITNEARCLIP;
  9238.     clip[1] = INITFARCLIP;
  9239.     clipArray = NewRealArray(1, 2L);
  9240.     CArray2Array(clipArray, clip);
  9241.     }
  9242.     {
  9243.     ObjPtr anArray;
  9244.     real posn[3];
  9245.     posn[0] = 0.0;
  9246.     posn[1] = 0.0;
  9247.     posn[2] = INITEYEDIST;
  9248.     anArray = NewRealArray(1, 3L);
  9249.     CArray2Array(anArray, posn);
  9250.     SetVar(observerClass, LOCATION, anArray);
  9251.     posn[0] = 0.0;
  9252.     posn[1] = 0.0;
  9253.     posn[2] = -1.0;
  9254.     anArray = NewRealArray(1, 3L);
  9255.     CArray2Array(anArray, posn);
  9256.     SetVar(observerClass, FORWARDVECTOR, anArray);
  9257.     posn[0] = 0.0;
  9258.     posn[1] = 1.0;
  9259.     posn[2] = 0.0;
  9260.     anArray = NewRealArray(1, 3L);
  9261.     CArray2Array(anArray, posn);
  9262.     SetVar(observerClass, UPVECTOR, anArray);
  9263.     posn[0] = 0.0;
  9264.     posn[1] = 0.0;
  9265.     posn[2] = 0.0;
  9266.     anArray = NewRealArray(1, 3L);
  9267.     CArray2Array(anArray, posn);
  9268.     SetVar(observerClass, SPACEORIGIN, anArray);
  9269.     SetVar(observerClass, SPACESCALE, NewReal(1.0));
  9270.     }
  9271.     SetVar(observerClass, FOCUSDIST, NewReal((real) INITEYEDIST));
  9272.  
  9273.     SetMethod(observerClass, MARKTIME, WakeObserver);
  9274.     icon = NewIcon(0, 0, ICONOBSERVER, "Observer");
  9275.     SetVar(icon, HELPSTRING, 
  9276.     NewString("This icon represents an observer.  The observer represents \
  9277. you looking into the 3-dimensional space.  You can change attributes such as the \
  9278. viewing angle and near and far clipping planes in the control panel, which you \
  9279. can show by selecting the icon and choosing the Show Controls \
  9280. item in the Object menu.\n\
  9281. \n\
  9282. You can drag this icon into the icon corral of another visualization window to have \
  9283. it control the other space as well.  When an observer controls more than one \
  9284. space, the view of the objects in the spaces are tied together.  This is very \
  9285. useful for viewing several similar datasets from the same viewpoint at once.  \
  9286. A space can only be controlled by one observer at a time.  If you drag another \
  9287. observer icon into this space, this observer will be replaced."));
  9288. #if 0
  9289.     SetMethod(icon, ICONEXTRADRAW, DrawExtraObserverIcon);
  9290. #endif
  9291.     SetVar(observerClass, DEFAULTICON, icon);
  9292.     SetVar(observerClass, CLASSID, NewInt(CLASS_OBSERVER));
  9293.     SetVar(observerClass, ONEONLY, ObjTrue);
  9294.     SetVar(observerClass, NAME, NewString("Observer"));
  9295.     SetVar(observerClass, AUTOADJUST, NewInt(1));
  9296.     SetVar(observerClass, USESPACECOORDS, NewInt(0));
  9297.     SetMethod(observerClass, CLONE, CloneObserver);
  9298.     SetVar(observerClass, BINOCULARITY, NewReal(0.4));
  9299.  
  9300.     /*Create class of renderers*/
  9301.     rendererClass = NewObject(controllerClass, 0);
  9302.     AddToReferenceList(rendererClass);
  9303.     SetMethod(rendererClass, BINDTOSPACE, BindRendererToSpace);
  9304.     SetMethod(rendererClass, NEWCTLWINDOW, ShowRendererControls);
  9305.     SetVar(rendererClass, DOUBLECLICK, NewString(OF_SHOW_CONTROLS));
  9306.     SetVar(rendererClass, CLASSID, NewInt(CLASS_RENDERER));
  9307.     SetVar(rendererClass, ONEONLY, ObjTrue);
  9308.     SetVar(rendererClass, RENDERTYPE, NewInt(hasRGB ? RT_RGB_HARDWARE : RT_CMAP_HARDWARE));
  9309.     SetVar(rendererClass, FILTERTYPE, NewInt(RT_NONE));
  9310.     icon = NewIcon(0, 0, ICONRENDERER, "Renderer");
  9311.     SetVar(icon, HELPSTRING,
  9312.     NewString("This icon represents a renderer.  The controls the process \
  9313. of rendering, or producing an image from the visualization objects in the space.  \
  9314. You can show controls for the renderer by selecting the icon and choosing the Show Controls \
  9315. item in the Object menu.\n\
  9316. \n\
  9317. You can drag this icon into the icon corral of another visualization window to have \
  9318. it control the other space as well.  \
  9319. A space can only be controlled by one renderer at a time.  If you drag another \
  9320. renderer icon into this space, this renderer will be replaced."));
  9321.     SetVar(rendererClass, DEFAULTICON, icon);
  9322.     SetVar(rendererClass, NAME, NewString("Renderer"));
  9323.     SetMethod(rendererClass, CLONE, CloneRenderer);
  9324.  
  9325.     /*Make a placement track class*/
  9326.     placementTrackClass = NewObject(subTrackClass, 0L);
  9327.     AddToReferenceList(placementTrackClass);
  9328.     SetMethod(placementTrackClass, EXPRESS, ExpressObserverPlacement);
  9329.     DeclareDependency(placementTrackClass, JOINTS, CHANGED);
  9330.     SetMethod(placementTrackClass, JOINTS, MakePlacementJoints);
  9331.  
  9332.     /*Make joint class*/
  9333.     jointClass = NewObject(NULLOBJ, 0L);
  9334.     AddToReferenceList(jointClass);
  9335.  
  9336.     InitLights();
  9337. }
  9338.  
  9339. void KillSpaces()
  9340. /*Kills the spaces*/
  9341. {
  9342.     KillLights();
  9343.     RemoveFromReferenceList(jointClass);
  9344.     RemoveFromReferenceList(placementTrackClass);
  9345.     RemoveFromReferenceList(rendererClass);
  9346.     RemoveFromReferenceList(observerClass);
  9347.     RemoveFromReferenceList(stereoClass);
  9348.     RemoveFromReferenceList(placementClass);
  9349.     RemoveFromReferenceList(clockClass);
  9350.     RemoveFromReferenceList(controllerClass);
  9351.     RemoveFromReferenceList(spaceBackPanelClass);
  9352.     RemoveFromReferenceList(spacePanelClass);
  9353.     RemoveFromReferenceList(spaceClass);
  9354. }
  9355.